Skip to content

Conversation

@randolf-scholz
Copy link
Contributor

@randolf-scholz randolf-scholz commented Aug 7, 2025

New Tests

  • JoinSuite.test_mixed_literal_types tests join between Literal? and other types.
  • MeetSuite.test_mixed_literal_types tests meet between Literal? and other types
  • TypeOpsSuite.test_simplified_union_with_mixed_str_literals2 tests simplified unions containing Literal? types
  • SubtypingSuite.test_literal tests subtype checks with Literal? types
  • RestrictionSuite: new test suite for testing the restrict_subtype_away method.
  • testJoinLiteralInstanceAndEnum tests join between Literal? and StrEnum

@github-actions

This comment has been minimized.

@randolf-scholz
Copy link
Contributor Author

The first operator failure seems like a bug in typeshed to me: python/typeshed#9004

@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@randolf-scholz
Copy link
Contributor Author

randolf-scholz commented Aug 7, 2025

Hm, the xarray redundant cast looks like this: mypy-playground

from typing import Literal, cast

type PDDatetimeUnitOptions = Literal["s", "ms", "ns"]

time_unit = cast(PDDatetimeUnitOptions, "s")  # [redundant-cast]

Is the current behavior of emitting redundant cast here intentional? It seems formally incorrect, since "s" should be Literal["s"]? and not Literal["s", "ms", "ns"], but maybe this was introduces as a convenience thing?

In comparison, pyright does not issue RedundantCast here, which aligns with my priors. pyright-playground

@randolf-scholz randolf-scholz marked this pull request as ready for review August 8, 2025 06:29
@randolf-scholz randolf-scholz marked this pull request as draft August 8, 2025 06:29
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@randolf-scholz
Copy link
Contributor Author

Repro for the pyinstrument change:

from typing import TypedDict, Literal

type AsyncMode = Literal["enabled", "disabled", "strict"]

class Options(TypedDict, total=False):
    async_mode: AsyncMode

def test(kwargs: Options, d: dict[Literal["async_mode"], AsyncMode]) -> None:
    reveal_type(kwargs)
    reveal_type(kwargs.get)
    reveal_type(d.get)
    reveal_type(kwargs.get("async_mode", "disabled"))
    # master: AsyncMode
    # feature: "Literal['enabled'] | Literal['strict'] | Literal['disabled']?"

I believe the problem here is not the new union schema ("x"? | "x" --> "x"? rather than "x"; see #19625), but that mypy is too simplistic about kwargs.get.

reveal_type(kwargs.get)
# mypy: Overload
#        def (str) -> object,
#        def (str, default: object) -> object,
#        def [_T] (str, default: _T`1) -> object)"
# pyright: Overload
#        def (k: Literal['async_mode']) -> (AsyncMode | None)
#        def [T] (k: Literal['async_mode'], default: T | AsyncMode) -> (T | AsyncMode)
#        def [T] (k: str) -> (Any | None), (k: str, default: Any | T) -> (Any | T)

Basically, we want here to pick the second signature shown by pyright, and use the minimal solution T=Never, or equivalently an overload of the form

def [T] (k: Literal['async_mode'], default: AsyncMode) -> (AsyncMode)

@github-actions

This comment has been minimized.

@github-actions
Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

prefect (https://github.com/PrefectHQ/prefect)
+ src/prefect/task_worker.py:370: error: Argument 1 to "submit" of "Executor" has incompatible type "Callable[[Callable[_P, _T], **_P], _T]"; expected "Callable[[Callable[[Task[P, R], Optional[UUID], Optional[TaskRun], Optional[dict[str, Any]], Union[PrefectFuture[Any], Any, Iterable[Union[PrefectFuture[Any], Any]], None], Literal['state', 'result'], Optional[dict[str, set[RunInput]]], Optional[dict[str, Any]]], Union[R, State[Any], None]], Task[[VarArg(Any), KwArg(Any)], Any], UUID, TaskRun, dict[Any, Any], list[Any], str, Optional[Any]], Union[Any, State[Any], None]]"  [arg-type]

strawberry (https://github.com/strawberry-graphql/strawberry)
- strawberry/codegen/query_codegen.py:476: error: Redundant cast to "Literal['query', 'mutation', 'subscription']"  [redundant-cast]

operator (https://github.com/canonical/operator)
+ ops/model.py:832: error: Unsupported operand types for - ("set[tuple[Literal['tcp', 'udp', 'icmp'], int | None]]" and "set[tuple[str, int] | tuple[Literal['tcp', 'udp', 'icmp'], int | None]]")  [operator]
+ ops/model.py:834: error: Incompatible types in assignment (expression has type "str", variable has type "Literal['tcp', 'udp', 'icmp']")  [assignment]

core (https://github.com/home-assistant/core)
+ homeassistant/data_entry_flow.py:646: error: Unused "type: ignore" comment  [unused-ignore]
+ homeassistant/data_entry_flow.py:651: error: Unused "type: ignore" comment  [unused-ignore]
- homeassistant/components/evohome/storage.py:98: error: Argument 1 to "fromisoformat" of "date" has incompatible type "object"; expected "str"  [arg-type]

discord.py (https://github.com/Rapptz/discord.py)
- discord/scheduled_event.py:139: error: Incompatible types in assignment (expression has type "object", variable has type "str | None")  [assignment]
- discord/scheduled_event.py:145: error: Incompatible types in assignment (expression has type "object", variable has type "str | None")  [assignment]
- discord/scheduled_event.py:146: error: Incompatible types in assignment (expression has type "object", variable has type "int")  [assignment]
- discord/scheduled_event.py:150: error: Argument 1 to "store_user" of "ConnectionState" has incompatible type "object"; expected "User | PartialUser"  [arg-type]
- discord/scheduled_event.py:155: error: No overload variant of "parse_time" matches argument type "object"  [call-overload]
- discord/scheduled_event.py:155: note: Possible overload variants:
- discord/scheduled_event.py:155: note:     def parse_time(timestamp: None) -> None
- discord/scheduled_event.py:155: note:     def parse_time(timestamp: str) -> datetime
- discord/scheduled_event.py:155: note:     def parse_time(timestamp: str | None) -> datetime | None
- discord/scheduled_event.py:159: error: Argument 1 to "_unroll_metadata" of "ScheduledEvent" has incompatible type "object"; expected "EntityMetadata | None"  [arg-type]
- discord/app_commands/models.py:224: error: No overload variant of "int" matches argument type "object"  [call-overload]
- discord/app_commands/models.py:224: note: Possible overload variants:
- discord/app_commands/models.py:224: note:     def __new__(cls, str | Buffer | SupportsInt | SupportsIndex | SupportsTrunc = ..., /) -> int
- discord/app_commands/models.py:224: note:     def __new__(cls, str | bytes | bytearray, /, base: SupportsIndex) -> int
- discord/app_commands/models.py:231: error: Incompatible types in assignment (expression has type "object", variable has type "bool")  [assignment]
- discord/app_commands/models.py:237: error: Argument 1 to "_from_value" of "ArrayFlags" has incompatible type "object"; expected "Sequence[int]"  [arg-type]
- discord/app_commands/models.py:243: error: Argument 1 to "_from_value" of "ArrayFlags" has incompatible type "object"; expected "Sequence[int]"  [arg-type]
- discord/app_commands/models.py:245: error: Incompatible types in assignment (expression has type "object", variable has type "bool")  [assignment]
- discord/app_commands/models.py:246: error: Argument 1 to "_to_locale_dict" has incompatible type "object"; expected "dict[str, str]"  [arg-type]
- discord/app_commands/models.py:247: error: Argument 1 to "_to_locale_dict" has incompatible type "object"; expected "dict[str, str]"  [arg-type]
- discord/app_commands/models.py:1065: error: Argument 1 to "_to_locale_dict" has incompatible type "object"; expected "dict[str, str]"  [arg-type]
- discord/app_commands/models.py:1066: error: Argument 1 to "_to_locale_dict" has incompatible type "object"; expected "dict[str, str]"  [arg-type]
- discord/app_commands/models.py:1164: error: Argument 1 to "_to_locale_dict" has incompatible type "object"; expected "dict[str, str]"  [arg-type]
- discord/app_commands/models.py:1165: error: Argument 1 to "_to_locale_dict" has incompatible type "object"; expected "dict[str, str]"  [arg-type]
- discord/message.py:868: error: "object" has no attribute "items"  [attr-defined]
- discord/app_commands/transformers.py:139: error: Incompatible types in assignment (expression has type "list[dict[str, Any]]", target has type "str | int")  [assignment]
+ discord/app_commands/transformers.py:139: error: Incompatible types in assignment (expression has type "list[dict[str, Any]]", target has type "bool | str | int")  [assignment]
- discord/app_commands/transformers.py:141: error: Incompatible types in assignment (expression has type "list[int]", target has type "str | int")  [assignment]
+ discord/app_commands/transformers.py:141: error: Incompatible types in assignment (expression has type "list[int]", target has type "bool | str | int")  [assignment]
- discord/app_commands/transformers.py:149: error: Incompatible types in assignment (expression has type "int | float", target has type "str | int")  [assignment]
+ discord/app_commands/transformers.py:149: error: Incompatible types in assignment (expression has type "int | float", target has type "bool | str | int")  [assignment]
- discord/app_commands/transformers.py:151: error: Incompatible types in assignment (expression has type "int | float", target has type "str | int")  [assignment]
+ discord/app_commands/transformers.py:151: error: Incompatible types in assignment (expression has type "int | float", target has type "bool | str | int")  [assignment]

dedupe (https://github.com/dedupeio/dedupe)
- dedupe/api.py:1547: error: Redundant cast to "Literal['match', 'distinct']"  [redundant-cast]

AutoSplit (https://github.com/Toufool/AutoSplit)
-   File "/tmp/mypy_primer/mypy_old/venv/bin/mypy", line 7, in <module>
+   File "/tmp/mypy_primer/mypy_new/venv/bin/mypy", line 7, in <module>
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/__main__.py", line 15, in console_entry
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/__main__.py", line 15, in console_entry
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/main.py", line 127, in main
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/main.py", line 127, in main
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/main.py", line 211, in run_build
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/main.py", line 211, in run_build
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/build.py", line 196, in build
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/build.py", line 196, in build
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/build.py", line 272, in _build
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/build.py", line 272, in _build
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/build.py", line 2946, in dispatch
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/build.py", line 2946, in dispatch
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/build.py", line 3346, in process_graph
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/build.py", line 3346, in process_graph
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/build.py", line 3475, in process_stale_scc
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/build.py", line 3475, in process_stale_scc
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/build.py", line 2493, in write_cache
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/build.py", line 2493, in write_cache
-   File "/tmp/mypy_primer/mypy_old/venv/lib/python3.13/site-packages/mypy/cache.py", line 28, in __init__
+   File "/tmp/mypy_primer/mypy_new/venv/lib/python3.13/site-packages/mypy/cache.py", line 28, in __init__

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Specify behavior for Literal? types. meet_types gives unexpected results when meeting literal and Instance

1 participant