-
Notifications
You must be signed in to change notification settings - Fork 0
fix: pep 695 named alias union fix #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,7 @@ | |
| Sequence as SequenceOld, # pyright: ignore[reportDeprecated] | ||
| ) | ||
|
|
||
| from typing_extensions import NotRequired, Required, TypeForm, override | ||
| from typing_extensions import NotRequired, Required, TypeAliasType, TypeForm, override | ||
|
|
||
| __all__ = [] | ||
|
|
||
|
|
@@ -189,6 +189,18 @@ def _untraced_validate( | |
| x: JsonValue, cls: TypeForm[T], *, type_vars: dict[int, TypeForm[object]] | ||
| ) -> T: | ||
| # todo(maximsmol): improve error messages with generics | ||
| alias_origin = get_origin(cls) | ||
| if isinstance(alias_origin, TypeAliasType): | ||
| type_vars2 = {**type_vars} | ||
| for parameter, argument in zip( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this duplicates code that handles |
||
| alias_origin.__type_params__, get_args(cls), strict=True | ||
| ): | ||
| type_vars2[id(parameter)] = argument | ||
| return _untraced_validate(x, alias_origin.__value__, type_vars=type_vars2) | ||
|
|
||
| if isinstance(cls, TypeAliasType): | ||
| return _untraced_validate(x, cls.__value__, type_vars=type_vars) | ||
|
|
||
| if isinstance(cls, TypeVar): | ||
| ref = type_vars.get(id(cls)) | ||
| if ref is None: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,11 @@ | ||
| import typing | ||
| from collections.abc import Mapping, Sequence | ||
| from dataclasses import dataclass | ||
| from enum import Enum | ||
|
|
||
| import pytest | ||
| from syrupy.extensions.json import JSONSnapshotExtension | ||
| from typing_extensions import NotRequired, Required, TypedDict, TypeVar | ||
| from typing_extensions import NotRequired, Required, TypeAliasType, TypedDict, TypeVar | ||
|
|
||
| BrokenJsonArray: typing.TypeAlias = typing.Sequence["BrokenJsonValue"] # pyright: ignore[reportDeprecated] | ||
| BrokenJsonObject: typing.TypeAlias = typing.Mapping[str, "BrokenJsonValue"] # pyright: ignore[reportDeprecated] | ||
|
|
@@ -18,6 +19,19 @@ | |
| BrokenJsonObject2 | BrokenJsonArray2 | str | int | float | bool | None | ||
| ) | ||
|
|
||
| TestWaitingReason = TypeAliasType( | ||
| "TestWaitingReason", typing.Literal["workspace_capacity", "taiga_rate_limited"] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make this a much simpler test that doesn't also overlap with dataclasses support etc. unless there is a reason for it? also don't pull in actual names/scenarios from other projects here |
||
| ) | ||
|
|
||
|
|
||
| @dataclass | ||
| class AliasedRequest: | ||
| waiting_reason: TestWaitingReason | None | ||
|
|
||
|
|
||
| AliasT = TypeVar("AliasT") | ||
| TestBox = TypeAliasType("TestBox", list[AliasT], type_params=(AliasT,)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move these test resources into the test function rather than defining globally
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO, test this only on Python 3.12+ since that's when the nobody is going around making which is transparent at runtime |
||
|
|
||
|
|
||
| from latch_data_validation.data_validation import ( | ||
| DataValidationError, | ||
|
|
@@ -157,6 +171,24 @@ class Test: | |
| assert x.c.a == 2 | ||
|
|
||
|
|
||
| def test_type_alias() -> None: | ||
| assert validate( | ||
| {"waiting_reason": "workspace_capacity"}, AliasedRequest | ||
| ) == AliasedRequest(waiting_reason="workspace_capacity") | ||
| assert validate({"waiting_reason": None}, AliasedRequest) == AliasedRequest( | ||
| waiting_reason=None | ||
| ) | ||
|
|
||
| with pytest.raises(DataValidationError): | ||
| _ = validate({"waiting_reason": "unknown"}, AliasedRequest) | ||
|
|
||
|
|
||
| def test_generic_type_alias() -> None: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. merge this into |
||
| assert validate([1, 2], TestBox[int]) == [1, 2] | ||
| with pytest.raises(DataValidationError): | ||
| _ = validate([1, "two"], TestBox[int]) | ||
|
|
||
|
|
||
| def test_forwardref(snapshot_json) -> None: | ||
| _ = validate({"a": 123}, JsonValue) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we already run
get_originlater, don't run it twiceprobably just move this code down unless it really needs to go first (in which case move the
get_origincall up instead)