diff --git a/altair/utils/schemapi.py b/altair/utils/schemapi.py index 9fe29c2cf..4bb04cb29 100644 --- a/altair/utils/schemapi.py +++ b/altair/utils/schemapi.py @@ -734,29 +734,39 @@ def __eq__(self, other): and self._kwds == other._kwds ) - def to_dict(self, validate=True, ignore=None, context=None): + def to_dict( + self, + validate: bool = True, + ignore: Optional[List[str]] = None, + context: Optional[Dict[str, Any]] = None, + ) -> dict: """Return a dictionary representation of the object Parameters ---------- - validate : boolean + validate : bool, optional If True (default), then validate the output dictionary against the schema. - ignore : list - A list of keys to ignore. This will *not* passed to child to_dict - function calls. - context : dict (optional) - A context dictionary that will be passed to all child to_dict - function calls + ignore : list[str], optional + A list of keys to ignore. It is usually not needed + to specify this argument as a user. + context : dict[str, Any], optional + A context dictionary. It is usually not needed + to specify this argument as a user. + + Notes + ----- + Technical: The ignore parameter will *not* be passed to child to_dict + function calls. Returns ------- - dct : dictionary + dict The dictionary representation of this object Raises ------ - jsonschema.ValidationError : + SchemaValidationError : if validate=True and the dict does not conform to the schema """ if context is None: @@ -816,36 +826,41 @@ def to_dict(self, validate=True, ignore=None, context=None): def to_json( self, - validate=True, - ignore=None, - context=None, - indent=2, - sort_keys=True, + validate: bool = True, + indent: int = 2, + sort_keys: bool = True, + ignore: Optional[List[str]] = None, + context: Optional[Dict[str, Any]] = None, **kwargs, - ): + ) -> str: """Emit the JSON representation for this object as a string. Parameters ---------- - validate : boolean + validate : bool, optional If True (default), then validate the output dictionary against the schema. - ignore : list (optional) - A list of keys to ignore. This will *not* passed to child to_dict - function calls. - context : dict (optional) - A context dictionary that will be passed to all child to_dict - function calls - indent : integer, default 2 - the number of spaces of indentation to use - sort_keys : boolean, default True - if True, sort keys in the output + indent : int, optional + The number of spaces of indentation to use. The default is 2. + sort_keys : bool, optional + If True (default), sort keys in the output. + ignore : list[str], optional + A list of keys to ignore. It is usually not needed + to specify this argument as a user. + context : dict[str, Any], optional + A context dictionary. It is usually not needed + to specify this argument as a user. **kwargs Additional keyword arguments are passed to ``json.dumps()`` + Notes + ----- + Technical: The ignore parameter will *not* be passed to child to_dict + function calls. + Returns ------- - spec : string + str The JSON specification of the chart object. """ if ignore is None: diff --git a/altair/vegalite/v5/api.py b/altair/vegalite/v5/api.py index ed449bcab..8418b79e4 100644 --- a/altair/vegalite/v5/api.py +++ b/altair/vegalite/v5/api.py @@ -8,10 +8,11 @@ from toolz.curried import pipe as _pipe import itertools import sys -from typing import cast +from typing import cast, List, Optional, Any # Have to rename it here as else it overlaps with schema.core.Type from typing import Type as TypingType +from typing import Dict as TypingDict from .schema import core, channels, mixins, Undefined, SCHEMA_URL @@ -815,8 +816,41 @@ class TopLevelMixin(mixins.ConfigMethodMixin): _class_is_valid_at_instantiation = False - def to_dict(self, *args, **kwargs) -> dict: - """Convert the chart to a dictionary suitable for JSON export""" + def to_dict( + self, + validate: bool = True, + ignore: Optional[List[str]] = None, + context: Optional[TypingDict[str, Any]] = None, + ) -> dict: + """Convert the chart to a dictionary suitable for JSON export + + Parameters + ---------- + validate : bool, optional + If True (default), then validate the output dictionary + against the schema. + ignore : list[str], optional + A list of keys to ignore. It is usually not needed + to specify this argument as a user. + context : dict[str, Any], optional + A context dictionary. It is usually not needed + to specify this argument as a user. + + Notes + ----- + Technical: The ignore parameter will *not* be passed to child to_dict + function calls. + + Returns + ------- + dict + The dictionary representation of this chart + + Raises + ------ + SchemaValidationError + if validate=True and the dict does not conform to the schema + """ # We make use of three context markers: # - 'data' points to the data that should be referenced for column type # inference. @@ -827,7 +861,7 @@ def to_dict(self, *args, **kwargs) -> dict: # note: not a deep copy because we want datasets and data arguments to # be passed by reference - context = kwargs.get("context", {}).copy() + context = context.copy() if context else {} context.setdefault("datasets", {}) is_top_level = context.get("top_level", True) @@ -842,12 +876,13 @@ def to_dict(self, *args, **kwargs) -> dict: # remaining to_dict calls are not at top level context["top_level"] = False - kwargs["context"] = context # TopLevelMixin instance does not necessarily have to_dict defined # but due to how Altair is set up this should hold. # Too complex to type hint right now - dct = super(TopLevelMixin, copy).to_dict(*args, **kwargs) # type: ignore[misc] + dct = super(TopLevelMixin, copy).to_dict( # type: ignore[misc] + validate=validate, ignore=ignore, context=context + ) # TODO: following entries are added after validation. Should they be validated? if is_top_level: @@ -2508,16 +2543,51 @@ def from_dict(cls, dct, validate=True) -> "Chart": # type: ignore[override] # # As a last resort, try using the Root vegalite object return core.Root.from_dict(dct, validate) - def to_dict(self, *args, **kwargs) -> dict: - """Convert the chart to a dictionary suitable for JSON export.""" - context = kwargs.get("context", {}) + def to_dict( + self, + validate: bool = True, + ignore: Optional[List[str]] = None, + context: Optional[TypingDict[str, Any]] = None, + ) -> dict: + """Convert the chart to a dictionary suitable for JSON export + + Parameters + ---------- + validate : bool, optional + If True (default), then validate the output dictionary + against the schema. + ignore : list[str], optional + A list of keys to ignore. It is usually not needed + to specify this argument as a user. + context : dict[str, Any], optional + A context dictionary. It is usually not needed + to specify this argument as a user. + + Notes + ----- + Technical: The ignore parameter will *not* be passed to child to_dict + function calls. + + Returns + ------- + dict + The dictionary representation of this chart + + Raises + ------ + SchemaValidationError + if validate=True and the dict does not conform to the schema + """ + context = context or {} if self.data is Undefined and "data" not in context: # No data specified here or in parent: inject empty data # for easier specification of datum encodings. copy = self.copy(deep=False) copy.data = core.InlineData(values=[{}]) - return super(Chart, copy).to_dict(*args, **kwargs) - return super().to_dict(*args, **kwargs) + return super(Chart, copy).to_dict( + validate=validate, ignore=ignore, context=context + ) + return super().to_dict(validate=validate, ignore=ignore, context=context) def add_params(self, *params) -> Self: """Add one or more parameters to the chart.""" diff --git a/tools/schemapi/schemapi.py b/tools/schemapi/schemapi.py index de3e20b16..7d1a73a19 100644 --- a/tools/schemapi/schemapi.py +++ b/tools/schemapi/schemapi.py @@ -732,29 +732,39 @@ def __eq__(self, other): and self._kwds == other._kwds ) - def to_dict(self, validate=True, ignore=None, context=None): + def to_dict( + self, + validate: bool = True, + ignore: Optional[List[str]] = None, + context: Optional[Dict[str, Any]] = None, + ) -> dict: """Return a dictionary representation of the object Parameters ---------- - validate : boolean + validate : bool, optional If True (default), then validate the output dictionary against the schema. - ignore : list - A list of keys to ignore. This will *not* passed to child to_dict - function calls. - context : dict (optional) - A context dictionary that will be passed to all child to_dict - function calls + ignore : list[str], optional + A list of keys to ignore. It is usually not needed + to specify this argument as a user. + context : dict[str, Any], optional + A context dictionary. It is usually not needed + to specify this argument as a user. + + Notes + ----- + Technical: The ignore parameter will *not* be passed to child to_dict + function calls. Returns ------- - dct : dictionary + dict The dictionary representation of this object Raises ------ - jsonschema.ValidationError : + SchemaValidationError : if validate=True and the dict does not conform to the schema """ if context is None: @@ -814,36 +824,41 @@ def to_dict(self, validate=True, ignore=None, context=None): def to_json( self, - validate=True, - ignore=None, - context=None, - indent=2, - sort_keys=True, + validate: bool = True, + indent: int = 2, + sort_keys: bool = True, + ignore: Optional[List[str]] = None, + context: Optional[Dict[str, Any]] = None, **kwargs, - ): + ) -> str: """Emit the JSON representation for this object as a string. Parameters ---------- - validate : boolean + validate : bool, optional If True (default), then validate the output dictionary against the schema. - ignore : list (optional) - A list of keys to ignore. This will *not* passed to child to_dict - function calls. - context : dict (optional) - A context dictionary that will be passed to all child to_dict - function calls - indent : integer, default 2 - the number of spaces of indentation to use - sort_keys : boolean, default True - if True, sort keys in the output + indent : int, optional + The number of spaces of indentation to use. The default is 2. + sort_keys : bool, optional + If True (default), sort keys in the output. + ignore : list[str], optional + A list of keys to ignore. It is usually not needed + to specify this argument as a user. + context : dict[str, Any], optional + A context dictionary. It is usually not needed + to specify this argument as a user. **kwargs Additional keyword arguments are passed to ``json.dumps()`` + Notes + ----- + Technical: The ignore parameter will *not* be passed to child to_dict + function calls. + Returns ------- - spec : string + str The JSON specification of the chart object. """ if ignore is None: diff --git a/tools/update_init_file.py b/tools/update_init_file.py index 9c4894df4..41bb8f4d8 100644 --- a/tools/update_init_file.py +++ b/tools/update_init_file.py @@ -6,7 +6,7 @@ import sys from pathlib import Path from os.path import abspath, dirname, join -from typing import TypeVar, Type, cast, List, Any +from typing import TypeVar, Type, cast, List, Any, Optional import black @@ -80,6 +80,8 @@ def _is_relevant_attribute(attr_name): or attr is List or attr is Any or attr is Literal + or attr is Optional + or attr_name == "TypingDict" ): return False else: