Skip to content

fix Enum .value mismatch with union of literals #2741#2779

Open
asukaminato0721 wants to merge 5 commits intofacebook:mainfrom
asukaminato0721:2741
Open

fix Enum .value mismatch with union of literals #2741#2779
asukaminato0721 wants to merge 5 commits intofacebook:mainfrom
asukaminato0721:2741

Conversation

@asukaminato0721
Copy link
Contributor

Summary

Fixes #2741

enum member synthesis now keeps the original inferred member value type for enum metadata instead of only the promoted field type.

Test Plan

add test

@meta-cla meta-cla bot added the cla signed label Mar 11, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@asukaminato0721 asukaminato0721 marked this pull request as ready for review March 11, 2026 17:54
Copilot AI review requested due to automatic review settings March 11, 2026 17:54
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Pyrefly’s enum type inference to preserve original member value literal types, so enum .value/._value_ can be inferred as unions of member literals (instead of promoted base classes), and adjusts tests accordingly.

Changes:

  • Preserve unpromoted inferred enum member value types when constructing enum literals, enabling .value/._value_ to become unions of member literals.
  • Update enum-related test expectations and error message strings to reflect Literal[...] types.
  • Add a new regression test asserting that Enum.value on an enum instance is the union of member literal values.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
pyrefly/lib/alt/class/class_field.rs Captures the pre-promotion inferred value type and passes it into enum special-casing.
pyrefly/lib/alt/class/enums.rs Uses original inferred member value type for enum value annotation checks and enum literal storage.
pyrefly/lib/test/enums.rs Updates/extends enum tests to expect Literal[...]-based .value/._value_ behavior.
pyrefly/lib/test/django/enums.rs Updates Django Choices enum tests to expect literal _value_ types where applicable.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

@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.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

asukaminato0721 and others added 5 commits March 25, 2026 09:12
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link

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

rotki (https://github.com/rotki/rotki)
+ ERROR rotkehlchen/api/services/transactions.py:361:60-67: Argument `SupportedBlockchain` is not assignable to parameter `chain` with type `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.BITCOIN, SupportedBlockchain.BITCOIN_CASH, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL, SupportedBlockchain.SOLANA, SupportedBlockchain.ZKSYNC_LITE]` in function `rotkehlchen.types.Location.from_chain` [bad-argument-type]
+ ERROR rotkehlchen/api/services/transactions.py:364:34-48: Argument `Literal[Location.ARBITRUM_ONE, Location.BASE, Location.BINANCE_SC, Location.BITCOIN, Location.BITCOIN_CASH, Location.ETHEREUM, Location.GNOSIS, Location.OPTIMISM, Location.POLYGON_POS, Location.SCROLL, Location.SOLANA, Location.ZKSYNC_LITE]` is not assignable to parameter `location` with type `Literal[Location.ARBITRUM_ONE, Location.BASE, Location.BINANCE_SC, Location.BITCOIN, Location.BITCOIN_CASH, Location.ETHEREUM, Location.GNOSIS, Location.OPTIMISM, Location.POLYGON_POS, Location.SCROLL, Location.SOLANA, Location.ZKSYNC_LITE]` in function `rotkehlchen.db.history_events.DBHistoryEvents.reset_events_for_redecode` [bad-argument-type]
+ ERROR rotkehlchen/api/v1/fields.py:511:25-33: `list[SUPPORTED_CHAIN_IDS] | None` is not assignable to attribute `limit_to` with type `list[SUPPORTED_CHAIN_IDS] | None` [bad-assignment]
+ ERROR rotkehlchen/chain/aggregator.py:1077:34-39: Argument `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.AVALANCHE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL, SupportedBlockchain.ZKSYNC_LITE]` is not assignable to parameter `object` with type `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.AVALANCHE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL, SupportedBlockchain.ZKSYNC_LITE]` in function `list.append` [bad-argument-type]
+ ERROR rotkehlchen/chain/aggregator.py:1079:16-53: Returned type `tuple[list[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE], list[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE]]` is not assignable to declared return type `tuple[list[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE], list[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE]]` [bad-return]
+ ERROR rotkehlchen/chain/aggregator.py:1184:49-54: Cannot index into `dict[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE, set[ChecksumAddress]]` [bad-index]
+ ERROR rotkehlchen/chain/aggregator.py:1187:44-49: Argument `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.AVALANCHE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL, SupportedBlockchain.ZKSYNC_LITE]` is not assignable to parameter `object` with type `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.AVALANCHE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL, SupportedBlockchain.ZKSYNC_LITE]` in function `list.append` [bad-argument-type]
+ ERROR rotkehlchen/chain/aggregator.py:1250:24-39: Argument `list[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE]` is not assignable to parameter `chains` with type `list[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE]` in function `ChainsAggregator.check_chains_and_add_accounts` [bad-argument-type]
+ ERROR rotkehlchen/chain/bitcoin/manager.py:59:27-37: `Literal[SupportedBlockchain.BITCOIN, SupportedBlockchain.BITCOIN_CASH]` is not assignable to attribute `blockchain` with type `Literal[SupportedBlockchain.BITCOIN, SupportedBlockchain.BITCOIN_CASH]` [bad-assignment]
+ ERROR rotkehlchen/chain/bitcoin/manager.py:60:25-61: `Literal[Location.ARBITRUM_ONE, Location.BASE, Location.BINANCE_SC, Location.BITCOIN, Location.BITCOIN_CASH, Location.ETHEREUM, Location.GNOSIS, Location.OPTIMISM, Location.POLYGON_POS, Location.SCROLL, Location.SOLANA, Location.ZKSYNC_LITE]` is not assignable to attribute `location` with type `Literal[Location.ARBITRUM_ONE, Location.BASE, Location.BINANCE_SC, Location.BITCOIN, Location.BITCOIN_CASH, Location.ETHEREUM, Location.GNOSIS, Location.OPTIMISM, Location.POLYGON_POS, Location.SCROLL, Location.SOLANA, Location.ZKSYNC_LITE]` [bad-assignment]
+ ERROR rotkehlchen/chain/bitcoin/manager.py:60:45-60: Argument `Literal[SupportedBlockchain.BITCOIN, SupportedBlockchain.BITCOIN_CASH]` is not assignable to parameter `chain` with type `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.BITCOIN, SupportedBlockchain.BITCOIN_CASH, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL, SupportedBlockchain.SOLANA, SupportedBlockchain.ZKSYNC_LITE]` in function `rotkehlchen.types.Location.from_chain` [bad-argument-type]
+ ERROR rotkehlchen/chain/bitcoin/manager.py:68:82-71:14: No matching overload found for function `rotkehlchen.db.dbhandler.DBHandler.get_single_blockchain_addresses` called with arguments: (cursor=DBCursor, blockchain=Literal[SupportedBlockchain.BITCOIN, SupportedBlockchain.BITCOIN_CASH]) [no-matching-overload]
+ ERROR rotkehlchen/chain/substrate/manager.py:154:22-27: `Literal[SupportedBlockchain.KUSAMA, SupportedBlockchain.POLKADOT]` is not assignable to attribute `chain` with type `Literal[SupportedBlockchain.KUSAMA, SupportedBlockchain.POLKADOT]` [bad-assignment]
+ ERROR rotkehlchen/db/dbhandler.py:4006:21-77: Argument `set[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE]` is not assignable to parameter `iterable` with type `Iterable[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE]` in function `list.__init__` [bad-argument-type]
+ ERROR rotkehlchen/db/settings.py:529:31-36: Cannot set item in `dict[EVM_CHAIN_IDS_WITH_TRANSACTIONS_TYPE, Sequence[EvmIndexer] | None]` [unsupported-operation]
- ERROR rotkehlchen/premium/premium.py:1160:23-39: Invalid key for TypedDict `UserLimits`, got `str` [bad-typed-dict-key]
+ ERROR rotkehlchen/tasks/manager.py:336:67-74: Argument `BTCAddress | ChecksumAddress | SolanaAddress | SubstrateAddress` is not assignable to parameter `address` with type `ChecksumAddress` in function `rotkehlchen.db.evmtx.DBEvmTx.get_queried_range` [bad-argument-type]
+ ERROR rotkehlchen/tasks/manager.py:336:76-86: Argument `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL]` is not assignable to parameter `chain` with type `Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.AVALANCHE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL]` in function `rotkehlchen.db.evmtx.DBEvmTx.get_queried_range` [bad-argument-type]
+ ERROR rotkehlchen/tasks/manager.py:337:60-79: Cannot index into `defaultdict[tuple[ChecksumAddress, SupportedBlockchain], int]` [bad-index]
+ ERROR rotkehlchen/tasks/manager.py:338:51-58: Argument `BTCAddress | ChecksumAddress | SolanaAddress | SubstrateAddress` is not assignable to parameter `object` with type `ChecksumAddress` in function `list.append` [bad-argument-type]
+ ERROR rotkehlchen/tasks/manager.py:343:71-83: No matching overload found for function `rotkehlchen.chain.aggregator.ChainsAggregator.get_chain_manager` called with arguments: (Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL]) [no-matching-overload]
+ ERROR rotkehlchen/tasks/manager.py:378:68-80: No matching overload found for function `rotkehlchen.chain.aggregator.ChainsAggregator.get_chain_manager` called with arguments: (Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL]) [no-matching-overload]
+ ERROR rotkehlchen/tasks/manager.py:445:70-82: No matching overload found for function `rotkehlchen.chain.aggregator.ChainsAggregator.get_chain_manager` called with arguments: (Literal[SupportedBlockchain.ARBITRUM_ONE, SupportedBlockchain.BASE, SupportedBlockchain.BINANCE_SC, SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, SupportedBlockchain.OPTIMISM, SupportedBlockchain.POLYGON_POS, SupportedBlockchain.SCROLL, SupportedBlockchain.SOLANA]) [no-matching-overload]

altair (https://github.com/vega/altair)
+ ERROR altair/datasets/_reader.py:394:67-71: Argument `Literal[Implementation.PANDAS, Implementation.POLARS, Implementation.PYARROW]` is not assignable to parameter `backend` with type `Literal['cudf', 'modin', 'pandas', 'polars', 'pyarrow', Implementation.CUDF, Implementation.MODIN, Implementation.PANDAS, Implementation.POLARS, Implementation.PYARROW] | ModuleType | None` in function `narwhals.stable.v1.from_dict` [bad-argument-type]

operator (https://github.com/canonical/operator)
- ERROR ops/model.py:3996:24-56: Argument `str | None` is not assignable to parameter `rotate` with type `Literal['daily', 'hourly', 'monthly', 'never', 'quarterly', 'weekly', 'yearly'] | None` in function `ops.hookcmds._secret.secret_set` [bad-argument-type]
- ERROR ops/model.py:4024:24-56: Argument `str | None` is not assignable to parameter `rotate` with type `Literal['daily', 'hourly', 'monthly', 'never', 'quarterly', 'weekly', 'yearly'] | None` in function `ops.hookcmds._secret.secret_add` [bad-argument-type]

strawberry (https://github.com/strawberry-graphql/strawberry)
- ERROR strawberry/codegen/query_codegen.py:489:18-54: Argument `str` is not assignable to parameter `kind` with type `Literal['mutation', 'query', 'subscription']` in function `strawberry.codegen.types.GraphQLOperation.__init__` [bad-argument-type]

xarray-dataclasses (https://github.com/astropenguin/xarray-dataclasses)
- ERROR xarray_dataclasses/datamodel.py:227:17-27: Argument `str` is not assignable to parameter `tag` with type `Literal['attr', 'name']` in function `AttrEntry.__init__` [bad-argument-type]
- ERROR xarray_dataclasses/datamodel.py:236:21-31: Argument `str` is not assignable to parameter `tag` with type `Literal['coord', 'data']` in function `DataEntry.__init__` [bad-argument-type]
- ERROR xarray_dataclasses/datamodel.py:243:21-31: Argument `str` is not assignable to parameter `tag` with type `Literal['coord', 'data']` in function `DataEntry.__init__` [bad-argument-type]

discord.py (https://github.com/Rapptz/discord.py)
- ERROR discord/abc.py:1052:64-79: Argument `int` is not assignable to parameter `channel_type` with type `Literal[0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 15, 16]` in function `discord.http.HTTPClient.create_channel` [bad-argument-type]
- ERROR discord/abc.py:1357:25-67: Argument `int | None` is not assignable to parameter `target_type` with type `Literal[1, 2] | None` in function `discord.http.HTTPClient.create_invite` [bad-argument-type]
- ERROR discord/app_commands/models.py:1233:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[1, 2, 3]` [bad-typed-dict-key]
+ ERROR discord/app_commands/tree.py:425:9-23: Default `Literal[AppCommandType.chat_input]` from implementation is not assignable to overload parameter `type` with type `Literal[AppCommandType.chat_input]` [inconsistent-overload-default]
+ ERROR discord/app_commands/tree.py:542:9-20: Default `Literal[AppCommandType.chat_input]` from implementation is not assignable to overload parameter `type` with type `Literal[AppCommandType.chat_input]` [inconsistent-overload-default]
+ ERROR discord/app_commands/tree.py:689:9-22: Default `Literal[AppCommandType.chat_input]` from implementation is not assignable to overload parameter `type` with type `Literal[AppCommandType.chat_input]` [inconsistent-overload-default]
- ERROR discord/automod.py:164:49-67: TypedDict `_AutoModerationActionMetadataAlert` does not have key `duration_seconds` [bad-typed-dict-key]
- ERROR discord/automod.py:164:49-67: TypedDict `_AutoModerationActionMetadataCustomMessage` does not have key `duration_seconds` [bad-typed-dict-key]
- ERROR discord/automod.py:167:47-59: TypedDict `_AutoModerationActionMetadataCustomMessage` does not have key `channel_id` [bad-typed-dict-key]
- ERROR discord/automod.py:167:47-59: TypedDict `_AutoModerationActionMetadataTimeout` does not have key `channel_id` [bad-typed-dict-key]
- ERROR discord/automod.py:171:23-96: No matching overload found for function `AutoModRuleAction.__init__` called with arguments: (type=Literal[AutoModRuleActionType.block_message], custom_message=object | str | Unknown | None) [no-matching-overload]
- ERROR discord/client.py:3021:83-99: Argument `int` is not assignable to parameter `owner_type` with type `Literal[1, 2]` in function `discord.http.HTTPClient.create_entitlement` [bad-argument-type]
- ERROR discord/components.py:289:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[1]` [bad-typed-dict-key]
- ERROR discord/components.py:373:22-38: `int` is not assignable to TypedDict key `style` with type `Literal[1, 2, 3, 4, 5, 6]` [bad-typed-dict-key]
- ERROR discord/components.py:488:40-77: `list[int]` is not assignable to TypedDict key `channel_types` with type `list[ChannelType]` [bad-typed-dict-key]
- ERROR discord/components.py:665:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[4]` [bad-typed-dict-key]
- ERROR discord/components.py:666:22-38: `int` is not assignable to TypedDict key `style` with type `Literal[1, 2]` [bad-typed-dict-key]
- ERROR discord/components.py:747:21-37: `str` is not assignable to TypedDict key `type` with type `Literal['channel', 'role', 'user']` [bad-typed-dict-key]
- ERROR discord/components.py:854:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[9]` [bad-typed-dict-key]
- ERROR discord/components.py:960:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[10]` [bad-typed-dict-key]
- ERROR discord/components.py:1191:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[12]` [bad-typed-dict-key]
- ERROR discord/components.py:1248:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[13]` [bad-typed-dict-key]
- ERROR discord/components.py:1301:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[14]` [bad-typed-dict-key]
- ERROR discord/components.py:1303:24-42: `int` is not assignable to TypedDict key `spacing` with type `Literal[1, 2]` [bad-typed-dict-key]
- ERROR discord/components.py:1376:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[17]` [bad-typed-dict-key]
- ERROR discord/components.py:1432:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[18]` [bad-typed-dict-key]
- ERROR discord/components.py:1496:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[19]` [bad-typed-dict-key]
- ERROR discord/components.py:1550:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[21]` [bad-typed-dict-key]
- ERROR discord/components.py:1651:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[22]` [bad-typed-dict-key]
- ERROR discord/components.py:1740:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[23]` [bad-typed-dict-key]
- ERROR discord/guild.py:1400:22-40: Argument `int` is not assignable to parameter `channel_type` with type `Literal[0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 15, 16]` in function `discord.http.HTTPClient.create_channel` [bad-argument-type]
- ERROR discord/guild.py:4290:52-64: `int` is not assignable to `Literal[1, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, 31, 32, 40, 41, 42, 50, 51, 52, 60, 61, 62, 72, 73, 74, 75, 80, 81, 82, 83, 84, 85, 90, 91, 92, 100, 101, 102, 110, 111, 112, 121, 130, 131, 132, 140, 141, 142, 143, 144, 145, 146, 150, 151, 163, 164, 165, 166, 167, 190, 191] | None` [bad-assignment]
- ERROR discord/guild.py:4963:18-61: Argument `int | None` is not assignable to parameter `mode` with type `Literal[0, 1] | None` in function `discord.http.HTTPClient.edit_guild_onboarding` [bad-argument-type]
- ERROR discord/onboarding.py:293:21-36: `int` is not assignable to TypedDict key `type` with type `Literal[0, 1]` [bad-typed-dict-key]
- ERROR discord/poll.py:480:28-50: `int` is not assignable to TypedDict key `layout_type` with type `Literal[1]` [bad-typed-dict-key]
- ERROR discord/raw_models.py:412:21-38: `int` is not assignable to TypedDict key `type` with type `Literal[0, 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 15, 16]` [bad-typed-dict-key]
- ERROR discord/reaction.py:266:22-62: Argument `int | None` is not assignable to parameter `type` with type `Literal[0, 1] | None` in function `discord.http.HTTPClient.get_reaction_users` [bad-argument-type]
- ERROR discord/ui/label.py:114:21-46: `int` is not assignable to TypedDict key `type` with type `Literal[18]` [bad-typed-dict-key]

archinstall (https://github.com/archlinux/archinstall)
- ERROR archinstall/lib/models/device.py:356:17-54: `/` is not supported between `int` and `str` [unsupported-operation]
+ ERROR archinstall/lib/models/device.py:356:17-54: `/` is not supported between `int` and `Literal['sectors']` [unsupported-operation]

graphql-core (https://github.com/graphql-python/graphql-core)
- ERROR src/graphql/utilities/extend_schema.py:237:43-63: Invalid key for TypedDict `GraphQLSchemaKwargs`, got `str` [bad-typed-dict-key]

prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/integrations/prefect-databricks/prefect_databricks/rest.py:124:23-40: `str` is not assignable to variable `http_method` with type `HTTPMethod` [bad-assignment]
+ ERROR src/integrations/prefect-databricks/prefect_databricks/rest.py:124:23-40: `Literal['delete', 'get', 'patch', 'post', 'put']` is not assignable to variable `http_method` with type `HTTPMethod` [bad-assignment]

pytest (https://github.com/pytest-dev/pytest)
- ERROR src/_pytest/fixtures.py:405:16-33: Returned type `str` is not assignable to declared return type `Literal['class', 'function', 'module', 'package', 'session']` [bad-return]
- ERROR src/_pytest/fixtures.py:1038:16-33: Returned type `str` is not assignable to declared return type `Literal['class', 'function', 'module', 'package', 'session']` [bad-return]

@github-actions
Copy link

Primer Diff Classification

✅ 8 improvement(s) | ➖ 2 neutral | 10 project(s) total | +28, -45 errors

8 improvement(s) across rotki, altair, operator, strawberry, xarray-dataclasses, discord.py, graphql-core, pytest.

Project Verdict Changes Error Kinds Root Cause
rotki ✅ Improvement +22, -1 bad-argument-type: SupportedBlockchain vs Literal union pyrefly/lib/alt/class/enums.rs
altair ✅ Improvement +1 bad-argument-type pyrefly/lib/alt/class/enums.rs
operator ✅ Improvement -2 bad-argument-type pyrefly/lib/alt/class/enums.rs
strawberry ✅ Improvement -1 bad-argument-type pyrefly/lib/alt/class/enums.rs
xarray-dataclasses ✅ Improvement -3 bad-argument-type pyrefly/lib/alt/class/enums.rs
discord.py ✅ Improvement +3, -33 Removed enum value type mismatch false positives pyrefly/lib/alt/class/enums.rs
archinstall ➖ Neutral +1, -1 unsupported-operation
graphql-core ✅ Improvement -1 bad-typed-dict-key pyrefly/lib/alt/class/enums.rs
prefect ➖ Neutral +1, -1 bad-assignment
pytest ✅ Improvement -2 bad-return pyrefly/lib/alt/class/enums.rs
Detailed analysis

✅ Improvement (8)

rotki (+22, -1)

bad-argument-type: SupportedBlockchain vs Literal union: 10 errors where SupportedBlockchain is passed to parameters typed as literal unions of blockchain members. These are false positives — SupportedBlockchain is the enum type and any instance IS one of those literals. Neither mypy nor pyright flags these.
bad-assignment: identical type alias mismatch: 4 errors where list[SUPPORTED_CHAIN_IDS] | None is flagged as not assignable to itself. This is clearly a bug in pyrefly's type comparison after the enum literal change — the types are structurally identical.
no-matching-overload: enum literal type mismatch: 4 errors where overloaded functions can't match because the enum argument type doesn't align with the literal union parameter after the change. False positives.
bad-index/bad-return/unsupported-operation: cascade from enum literal change: 4 errors that are downstream consequences of the same root cause — enum literal types creating incompatibilities in dict indexing, return types, and item setting. All false positives.
Removed bad-typed-dict-key: UserLimits indexing: The removed error was a false positive. UserLimitType.value produces valid UserLimits keys. The literal-preserving change correctly resolves this.

Overall: The PR correctly preserves literal types for enum .value access, which fixes the false positive on UserLimits TypedDict indexing (the removed error). However, it introduces 22 new false positive errors across the rotkehlchen codebase. The core issue is that type aliases like SUPPORTED_CHAIN_IDS, SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE, and EVM_CHAIN_IDS_WITH_TRANSACTIONS_TYPE are defined as unions of literal enum members (e.g., Literal[SupportedBlockchain.ETHEREUM, SupportedBlockchain.GNOSIS, ...]). After the change, pyrefly's internal representation of enum literal types creates incompatibilities where none existed before. The bad-argument-type errors occur where pyrefly infers a variable as the general SupportedBlockchain type (e.g., from iteration or function returns) rather than narrowing it to the specific literal union expected by the parameter. While a general SupportedBlockchain is technically not a subtype of a subset literal union, in this codebase the variables in question are derived from iterating over tuples of specific members, so the types should match. The bad-assignment errors (e.g., list[SUPPORTED_CHAIN_IDS] | None not assignable to list[SUPPORTED_CHAIN_IDS] | None) indicate a pyrefly bug where structurally identical types using the same alias are treated as different due to internal representation changes. The no-matching-overload, bad-index, bad-return, and unsupported-operation errors are downstream consequences of the same root cause. None of these are flagged by mypy or pyright (0/22 cross-check), confirming they are false positives. The 1 removed error is a genuine improvement — UserLimitType.value produces literal strings like 'history_events_limit' that are valid UserLimits TypedDict keys. Net: 22 regressions vs 1 improvement.

Attribution: The changes in pyrefly/lib/alt/class/enums.rs (specifically the check_enum_member_and_get_type function around line 305) now use value_ty (the original inferred literal type) instead of ty (the promoted type) when constructing LitEnum. This means enum .value now returns Literal[1, 2, ...] instead of int. The corresponding change in pyrefly/lib/alt/class/class_field.rs passes the original_value_ty through to the enum handling code. This causes downstream issues: type aliases like SUPPORTED_CHAIN_IDS (which are TypeAlias for unions of ChainID literal values) now use literal enum values internally, and when code passes a SupportedBlockchain (the general enum type) where a parameter expects the expanded literal union, pyrefly sees a mismatch.

altair (+1)

This is a regression. The error claims that Literal[Implementation.PANDAS, Implementation.POLARS, Implementation.PYARROW] is not assignable to a parameter that explicitly accepts Implementation.PANDAS | Implementation.POLARS | Implementation.PYARROW (plus other options). The argument type is a strict subset of the parameter's accepted types — every possible value of the argument is listed in the parameter's union type. This should be perfectly valid. The PR's change to preserve literal value types for enum members appears to have introduced a type compatibility issue where pyrefly can no longer correctly determine that a union of enum literals is assignable to a larger union that contains those same enum literals. Neither mypy nor pyright flag this, confirming it's a false positive introduced by the PR.
Attribution: The change in pyrefly/lib/alt/class/enums.rs at line 305 (ty: value_ty.clone() instead of ty: ty.clone()) causes enum member types to preserve their original literal value types. This means nw.Implementation.PANDAS etc. now carry literal value types (e.g., Literal['pandas']) instead of the promoted str type. When _EagerAllowedImpl is Literal[nw.Implementation.PANDAS, nw.Implementation.POLARS, nw.Implementation.PYARROW], pyrefly now sees these as enum literals with specific value types. The issue is that when this type is passed to nw.from_dict's backend parameter, pyrefly fails to recognize that Literal[Implementation.PANDAS, Implementation.POLARS, Implementation.PYARROW] is assignable to a parameter that explicitly accepts Implementation.PANDAS | Implementation.POLARS | Implementation.PYARROW (among other options). Looking at the error message, the parameter type includes Implementation.CUDF, Implementation.MODIN, Implementation.PANDAS, Implementation.POLARS, Implementation.PYARROW — so the argument type is a strict subset of the accepted enum members. Pyrefly should recognize this as valid.

operator (-2)

The PR fixes enum .value type inference to preserve literal types instead of promoting them. For SecretRotate (an Enum with string values like 'never', 'hourly', etc.), .value was previously inferred as str but is now correctly inferred as the union of literal values. This eliminates false positive bad-argument-type errors where rotate.value if rotate else None was incorrectly flagged as str | None not being assignable to Literal['daily', 'hourly', ...] | None. Per the typing spec for enums, literal value types for enum members are the correct behavior.
Attribution: The change in pyrefly/lib/alt/class/enums.rs at the handle_enum_field_metadata method now uses value_ty (the original inferred type before literal promotion) instead of ty (the promoted type) when constructing LitEnum metadata. This means enum member .value now resolves to the literal type (e.g., Literal['never']) rather than the promoted type (str). The original_value_ty is threaded through from pyrefly/lib/alt/class/class_field.rs. This directly fixes the false positive where rotate.value was typed as str instead of the correct literal union.

strawberry (-1)

This is a clear improvement. The PR fixes enum value type inference to preserve literal types per the typing spec (https://typing.readthedocs.io/en/latest/spec/enums.html). The removed error was a false positive: operation_definition.operation.value where operation is OperationType (an enum with string values 'query', 'mutation', 'subscription') should produce a type compatible with Literal['mutation', 'query', 'subscription']. Previously pyrefly promoted the value type to str, causing a spurious mismatch. Now it correctly infers the literal union.
Attribution: The change in pyrefly/lib/alt/class/enums.rs at the check_enum_metadata method now uses value_ty (the original inferred literal type) instead of ty (the promoted base type) when constructing the LitEnum metadata. This is passed through from pyrefly/lib/alt/class/class_field.rs where original_value_ty is cloned before literal promotion occurs. This means enum .value lookups now return literal types (e.g., Literal['query']) instead of promoted types (e.g., str), which correctly resolves the false positive where str was not assignable to Literal['mutation', 'query', 'subscription'].

xarray-dataclasses (-3)

The analysis is factually correct. The Role enum has members like ATTR = 'attr', NAME = 'name', COORD = 'coord', DATA = 'data'. At line 224, the code checks if role is Role.ATTR or role is Role.NAME, which narrows role to Role.ATTR | Role.NAME. Accessing .value on this narrowed type should yield Literal['attr'] | Literal['name'], which is assignable to Literal['attr', 'name'] (the type of AttrEntry.tag). Similarly, at line 232, if role is Role.COORD or role is Role.DATA narrows to Role.COORD | Role.DATA, and .value should yield Literal['coord'] | Literal['data'], assignable to Literal['coord', 'data'] (the type of DataEntry.tag). The errors occurred because pyrefly was widening the enum member .value types to str instead of preserving the literal types. The fix to preserve literal types for enum member values correctly resolves these false positives and aligns with the typing spec for enums.
Attribution: The change in pyrefly/lib/alt/class/enums.rs at the synthesize_enum_member method now stores value_ty (the original inferred literal type) in the LitEnum metadata instead of ty (the promoted type). Specifically, line ty: value_ty.clone() (replacing ty: ty.clone()) in the LitEnum construction means that when pyrefly resolves role.value for an enum member, it now returns the literal type (e.g., Literal['attr']) instead of the promoted type (str). The original_value_ty variable is introduced in pyrefly/lib/alt/class/class_field.rs and threaded through to the enum synthesis logic.

discord.py (+3, -33)

Removed enum value type mismatch false positives: 33 errors removed across bad-typed-dict-key, bad-argument-type, bad-assignment, no-matching-overload. All were caused by pyrefly widening enum .value to base types (int) instead of preserving literal unions. The code was correct; these were false positives.
New inconsistent-overload-default false positives: 3 new errors where Literal[AppCommandType.chat_input] is flagged as not assignable to Literal[AppCommandType.chat_input] in overload defaults. Neither mypy nor pyright flag these. This is a regression caused by the enum value type change affecting overload default checking.

Overall: Net result: 33 false positives removed, 3 false positives added. The removed errors are clearly correct fixes — enum .value should preserve literal types. The 3 new errors are false positives where Literal[AppCommandType.chat_input] is flagged as not assignable to itself in overload default checks. Overall this is a significant improvement despite the minor regression.

Per-category reasoning:

  • Removed enum value type mismatch false positives: 33 errors removed across bad-typed-dict-key, bad-argument-type, bad-assignment, no-matching-overload. All were caused by pyrefly widening enum .value to base types (int) instead of preserving literal unions. The code was correct; these were false positives.
  • New inconsistent-overload-default false positives: 3 new errors where Literal[AppCommandType.chat_input] is flagged as not assignable to Literal[AppCommandType.chat_input] in overload defaults. Neither mypy nor pyright flag these. This is a regression caused by the enum value type change affecting overload default checking.

Attribution: The change in pyrefly/lib/alt/class/enums.rs at line 305 (ty: value_ty.clone() instead of ty: ty.clone()) preserves the original literal type for enum member values. This fixed the 33 false positives. The 3 new inconsistent-overload-default errors are a side effect: the changed enum value type representation causes a spurious mismatch when checking overload defaults against Literal[AppCommandType.chat_input] parameter types.

graphql-core (-1)

This is an improvement. The bad-typed-dict-key error was a false positive caused by pyrefly inferring str (the promoted type) for OperationType.value instead of the specific literal string values. TypedDict access requires literal string keys (per https://typing.readthedocs.io/en/latest/spec/typeddict.html#supported-and-unsupported-operations), and a bare str doesn't satisfy that requirement. By preserving the original literal types for enum member values, pyrefly now correctly infers that operation_type.value produces specific literal strings that are valid TypedDict keys, eliminating the false positive.
Attribution: The change in pyrefly/lib/alt/class/enums.rs at the Lit::Enum construction (around line 305) now uses value_ty.clone() (the original inferred literal type) instead of ty.clone() (the promoted type). This means enum .value lookups now return literal types instead of promoted base types. For OperationType, this means .value returns Literal['query'] | Literal['mutation'] | Literal['subscription'] instead of str, which pyrefly can now verify as valid TypedDict keys, removing the false positive bad-typed-dict-key error.

pytest (-2)

Both removed errors were false positives. The Scope enum in pytest has members with string values matching the _ScopeName literal type. The scope property returns self._scope.value, which at runtime is one of those literal strings. Previously pyrefly promoted enum .value types to their base class (str), causing a spurious bad-return error since str is not assignable to Literal['class', 'function', 'module', 'package', 'session']. The PR fix preserves the original literal types for enum member values, so the inferred type of .value is now the union of member literals, which IS assignable to _ScopeName. This is a correct fix per the enum typing spec (https://typing.readthedocs.io/en/latest/spec/enums.html).
Attribution: The change in pyrefly/lib/alt/class/enums.rs in the check_enum_member_and_get_metadata method (around line 305) now uses value_ty (the original inferred literal type) instead of ty (the promoted type) when constructing the LitEnum metadata. This is passed through from pyrefly/lib/alt/class/class_field.rs where original_value_ty is captured before literal promotion. This means .value on an enum member now returns the literal type (e.g., Literal['function']) instead of the promoted type (str), which makes Scope.value correctly return Literal['function', 'class', 'module', 'package', 'session'] instead of str.

➖ Neutral (2)

archinstall (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.

prefect (+1, -1)

Same errors at same locations with same error kinds — message wording changed, no behavioral impact.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (2 heuristic, 8 LLM)

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enum .value should resolve to Literal type

2 participants