Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/HEAD' into refac-alt-theme
Browse files Browse the repository at this point in the history
  • Loading branch information
dangotbanned committed Oct 23, 2024
2 parents bf11ec1 + 5a6f70d commit afcd1b9
Show file tree
Hide file tree
Showing 8 changed files with 1,572 additions and 1,557 deletions.
118 changes: 59 additions & 59 deletions altair/vegalite/v5/schema/_config.py

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions altair/vegalite/v5/schema/_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"Orient_T",
"Orientation_T",
"PaddingKwds",
"PrimitiveValue_T",
"ProjectionType_T",
"RangeEnum_T",
"ResolveMode_T",
Expand Down Expand Up @@ -214,6 +215,7 @@ class PaddingKwds(TypedDict, total=False):
"vox",
]
Map: TypeAlias = Mapping[str, Any]
PrimitiveValue_T: TypeAlias = Union[str, bool, float, None]
AggregateOp_T: TypeAlias = Literal[
"argmax",
"argmin",
Expand Down
1,866 changes: 903 additions & 963 deletions altair/vegalite/v5/schema/channels.py

Large diffs are not rendered by default.

910 changes: 446 additions & 464 deletions altair/vegalite/v5/schema/core.py

Large diffs are not rendered by default.

114 changes: 57 additions & 57 deletions altair/vegalite/v5/schema/mixins.py

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ test = [
"ruff check .",
"ruff format --diff --check .",
"mypy altair tests",
"python -m pytest --pyargs --numprocesses=logical --doctest-modules tests altair",
"python -m pytest --pyargs --numprocesses=logical --doctest-modules tests altair tools",
]
test-coverage = "python -m pytest --pyargs --doctest-modules --cov=altair --cov-report term altair"
test-coverage-html = "python -m pytest --pyargs --doctest-modules --cov=altair --cov-report html altair"
Expand All @@ -130,18 +130,18 @@ update-init-file = [
]
test-fast = [
"ruff check .", "ruff format .",
"pytest -p no:randomly -n logical --numprocesses=logical --doctest-modules tests altair -m \"not slow\" {args}"
"pytest -p no:randomly -n logical --numprocesses=logical --doctest-modules tests altair tools -m \"not slow\" {args}"
]
test-slow = [
"ruff check .", "ruff format .",
"pytest -p no:randomly -n logical --numprocesses=logical --doctest-modules tests altair -m \"slow\" {args}"
"pytest -p no:randomly -n logical --numprocesses=logical --doctest-modules tests altair tools -m \"slow\" {args}"
]

[tool.hatch.envs.hatch-test]
# https://hatch.pypa.io/latest/tutorials/testing/overview/
features = ["all", "dev", "doc"]
# https://pytest-xdist.readthedocs.io/en/latest/distribution.html#running-tests-across-multiple-cpus
default-args = ["--numprocesses=logical","--doctest-modules", "tests", "altair"]
default-args = ["--numprocesses=logical","--doctest-modules", "tests", "altair", "tools"]
parallel = true
[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.9", "3.10", "3.11", "3.12"]
Expand Down
103 changes: 97 additions & 6 deletions tools/schemapi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,23 @@
"array": "list",
"null": "None",
}
_STDLIB_TYPE_NAMES = frozenset(
(
"int",
"float",
"str",
"None",
"bool",
"date",
"datetime",
"time",
"tuple",
"list",
"deque",
"dict",
"set",
)
)

_VALID_IDENT: Pattern[str] = re.compile(r"^[^\d\W]\w*\Z", re.ASCII)

Expand Down Expand Up @@ -146,6 +163,20 @@ def add_literal(
tp = f"Union[SchemaBase, {', '.join(it)}]"
return tp

def add_union(
self, info: SchemaInfo, tp_iter: Iterator[str], /, *, replace: bool = False
) -> str:
if title := info.title:
alias = self.fmt.format(title)
if alias not in self._aliases:
self.update_aliases(
(alias, f"Union[{', '.join(sort_type_reprs(tp_iter))}]")
)
return alias if replace else title
else:
msg = f"Unsupported operation.\nRequires a title.\n\n{info!r}"
raise NotImplementedError(msg)

def update_aliases(self, *name_statement: tuple[str, str]) -> None:
"""
Adds `(name, statement)` pairs to the definitions.
Expand Down Expand Up @@ -450,7 +481,7 @@ def to_type_repr( # noqa: C901
tp_str = TypeAliasTracer.add_literal(self, tp_str, replace=True)
tps.add(tp_str)
elif FOR_TYPE_HINTS and self.is_union_literal():
it = chain.from_iterable(el.literal for el in self.anyOf)
it: Iterator[str] = chain.from_iterable(el.literal for el in self.anyOf)
tp_str = TypeAliasTracer.add_literal(self, spell_literal(it), replace=True)
tps.add(tp_str)
elif self.is_anyOf():
Expand All @@ -459,6 +490,14 @@ def to_type_repr( # noqa: C901
for s in self.anyOf
)
tps.update(maybe_rewrap_literal(chain.from_iterable(it_nest)))
elif FOR_TYPE_HINTS and self.is_type_alias_union():
it = (
SchemaInfo(dict(self.schema, type=tp)).to_type_repr(
target=target, use_concrete=use_concrete
)
for tp in self.type
)
tps.add(TypeAliasTracer.add_union(self, it, replace=True))
elif isinstance(self.type, list):
# We always use title if possible for nested objects
tps.update(
Expand Down Expand Up @@ -764,6 +803,25 @@ def is_type_alias(self) -> bool:
and self.type in jsonschema_to_python_types
)

def is_type_alias_union(self) -> bool:
"""
Represents a name assigned to a list of literal types.
Example:
{"PrimitiveValue": {"type": ["number", "string", "boolean", "null"]}}
Translating from JSON -> Python, this is the same as an ``"anyOf"`` -> ``Union``.
The distinction in the schema is purely due to these types being defined in the draft, rather than definitions.
"""
TP = "type"
return (
self.schema.keys() == {TP}
and isinstance(self.type, list)
and bool(self.title)
)

def is_theme_config_target(self) -> bool:
"""
Return `True` for candidates classes in ``ThemeConfig`` hierarchy of ``TypedDict``(s).
Expand Down Expand Up @@ -859,14 +917,26 @@ def sort_type_reprs(tps: Iterable[str], /) -> list[str]:
We use `set`_ for unique elements, but the lack of ordering requires additional sorts:
- If types have same length names, order would still be non-deterministic
- Hence, we sort as well by type name as a tie-breaker, see `sort-stability`_.
- Using ``str.lower`` gives priority to `builtins`_ over ``None``.
- Lowest priority is given to generated aliases from ``TypeAliasTracer``.
- Using ``str.lower`` gives priority to `builtins`_.
- Lower priority is given to generated aliases from ``TypeAliasTracer``.
- These are purely to improve autocompletion
- ``None`` will always appear last.
Related
-------
- https://github.com/vega/altair/pull/3573#discussion_r1747121600
Examples
--------
>>> sort_type_reprs(["float", "None", "bool", "Chart", "float", "bool", "Chart", "str"])
['str', 'bool', 'float', 'Chart', 'None']
>>> sort_type_reprs(("None", "int", "Literal[5]", "int", "float"))
['int', 'float', 'Literal[5]', 'None']
>>> sort_type_reprs({"date", "int", "str", "datetime", "Date"})
['int', 'str', 'date', 'datetime', 'Date']
.. _set:
https://docs.python.org/3/tutorial/datastructures.html#sets
.. _sort-stability:
Expand All @@ -875,9 +945,30 @@ def sort_type_reprs(tps: Iterable[str], /) -> list[str]:
https://docs.python.org/3/library/functions.html
"""
dedup = tps if isinstance(tps, set) else set(tps)
it = sorted(dedup, key=str.lower) # Tertiary sort
it = sorted(it, key=len) # Secondary sort
return sorted(it, key=TypeAliasTracer.is_cached) # Primary sort
it = sorted(dedup, key=str.lower) # Quinary sort
it = sorted(it, key=len) # Quaternary sort
it = sorted(it, key=TypeAliasTracer.is_cached) # Tertiary sort
it = sorted(it, key=is_not_stdlib) # Secondary sort
it = sorted(it, key=is_none) # Primary sort
return it


def is_not_stdlib(s: str, /) -> bool:
"""
Sort key.
Places a subset of stdlib types at the **start** of list.
"""
return s not in _STDLIB_TYPE_NAMES


def is_none(s: str, /) -> bool:
"""
Sort key.
Always places ``None`` at the **end** of list.
"""
return s == "None"


def spell_nested_sequence(
Expand Down
8 changes: 4 additions & 4 deletions tools/vega_expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,14 +318,14 @@ class ReplaceMany:
--------
Providing a mapping during construction:
string = "The dog chased the cat, chasing the mouse. Poor mouse"
animal_replacer = ReplaceMany({"dog": "cat"})
>>> string = "The dog chased the cat, chasing the mouse. Poor mouse"
>>> animal_replacer = ReplaceMany({"dog": "cat"})
>>> animal_replacer(string)
'The cat chased the cat, chasing the mouse. Poor mouse'
Updating with new replacements:
animal_replacer.update({"cat": "mouse", "mouse": "dog"}, duck="rabbit")
>>> animal_replacer.update({"cat": "mouse", "mouse": "dog"}, duck="rabbit")
>>> animal_replacer(string, refresh=True)
'The cat chased the mouse, chasing the dog. Poor dog'
Expand Down Expand Up @@ -911,7 +911,7 @@ def italics_to_backticks(s: str, names: Iterable[str], /) -> str:
... "some text and *name* and more text but also *other* text",
... ("name", "other"),
... )
"some text and ``name`` and more text but also ``other`` text"
'some text and ``name`` and more text but also ``other`` text'
"""
pattern = rf"(?P<not_link_start>[^`_])\*(?P<name>{'|'.join(names)})\*(?P<not_link_end>[^`])"
return re.sub(pattern, r"\g<not_link_start>``\g<name>``\g<not_link_end>", s)
Expand Down

0 comments on commit afcd1b9

Please sign in to comment.