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.

asukaminato0721 and others added 5 commits March 18, 2026 12:05
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:

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]

git-revise (https://github.com/mystor/git-revise)
-  WARN gitrevise/odb.py:413:25-50: Redundant cast: `bytes` is the same type as `bytes` [redundant-cast]

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]

rotki (https://github.com/rotki/rotki)
+ ERROR rotkehlchen/api/services/transactions.py:351: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:354: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:20-78: No matching overload found for function `list.__init__` called with arguments: (set[SUPPORTED_EVM_EVMLIKE_CHAINS_TYPE]) [no-matching-overload]
+ ERROR rotkehlchen/db/settings.py:524: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:329: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:329: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:330:60-79: Cannot index into `defaultdict[tuple[ChecksumAddress, SupportedBlockchain], int]` [bad-index]
+ ERROR rotkehlchen/tasks/manager.py:331: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:336: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:371: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:438: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]

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:404: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:1022:16-33: Returned type `str` is not assignable to declared return type `Literal['class', 'function', 'module', 'package', 'session']` [bad-return]

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]

@github-actions
Copy link

Primer Diff Classification

❌ 3 regression(s) | ✅ 6 improvement(s) | ➖ 2 neutral | 11 project(s) total | +28, -46 errors

3 regression(s) across rotki, xarray-dataclasses, altair. error kinds: bad-argument-type, no-matching-overload, bad-assignment. 6 improvement(s) across strawberry, git-revise, operator, discord.py, graphql-core, pytest.

Project Verdict Changes Error Kinds Root Cause
strawberry ✅ Improvement -1 bad-argument-type pyrefly/lib/alt/class/enums.rs
git-revise ✅ Improvement -1 redundant-cast The changes to class_field.rs and enums.rs in the pyr...
operator ✅ Improvement -2 bad-argument-type pyrefly/lib/alt/class/class_field.rs
rotki ❌ Regression +22, -1 bad-argument-type pyrefly/lib/alt/class/class_field.rs
xarray-dataclasses ❌ Regression -3 bad-argument-type pyrefly/lib/alt/class/enums.rs
discord.py ✅ Improvement +3, -33 inconsistent-overload-default 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
altair ❌ Regression +1 bad-argument-type pyrefly/lib/alt/class/enums.rs
Detailed analysis

❌ Regression (3)

rotki (+22, -1)

bad-argument-type: Functions expecting unions of specific enum member literals now reject the enum type itself, even though at runtime the enum members ARE the expected values
no-matching-overload: Overloaded functions with literal enum parameters can't find matches when passed enum values
bad-assignment: Assignments involving enum types now fail due to literal type mismatches
bad-index: Dict indexing with enum keys fails when the dict expects specific literals
bad-return: Return type mismatches due to literal vs enum type differences
unsupported-operation: Dict operations fail due to enum literal type mismatches

Overall: The PR preserves literal types for enum member values (e.g., SupportedBlockchain.BITCOIN has value type Literal[SupportedBlockchain.BITCOIN] instead of promoted SupportedBlockchain). This causes type mismatches when passing enum values to functions expecting literal unions of specific enum members. For example, Location.from_chain() expects Literal[SupportedBlockchain.ARBITRUM_ONE, ...] but receives SupportedBlockchain. While preserving literals can be useful, applying this strictness to all enum usage creates many false positives that mypy/pyright don't flag. The errors show legitimate enum usage patterns that should work.

Attribution: The changes in pyrefly/lib/alt/class/class_field.rs and pyrefly/lib/alt/class/enums.rs now preserve the original inferred value type (original_value_ty) for enum members instead of using only the promoted type. This causes pyrefly to expect literal unions where the code passes the enum type itself.

xarray-dataclasses (-3)

Pyrefly now correctly infers enum .value as a union of member literals (per the PR's intent), but this creates false positives when the code uses enum identity checks that pyrefly cannot use for type narrowing. The removed errors were false positives - the code is type-safe at runtime.
Attribution: The change to preserve original literal types for enum values in pyrefly/lib/alt/class/enums.rs (specifically using value_ty instead of ty for enum member metadata) caused role.value to be inferred as a union of all member literals instead of just str.

altair (+1)

The PR intentionally changed enum member type inference to preserve literal types. The error occurs because pyrefly now infers Literal[Implementation.PANDAS, ...] for the enum members, but the narwhals.stable.v1.from_dict function expects a broader union that includes both the enum members AND their string literal values (e.g., both Implementation.PANDAS and 'pandas'). This is a false positive - the enum members should be assignable to a union containing those same enum members. The type checker is being overly strict about the literal type representation. This is a regression because the code is correct and would work at runtime.
Attribution: The changes in pyrefly/lib/alt/class/enums.rs modified enum member synthesis to preserve the original inferred member value type instead of only using the promoted field type. This affects how enum members are typed when used in literal contexts.

✅ Improvement (6)

strawberry (-1)

The PR improved enum type inference by preserving literal types for enum member values. Before, SomeEnum.MEMBER.value would return the base type (e.g., str), but now it correctly returns the literal type or union of literals (e.g., Literal['query', 'mutation', 'subscription']). This fixed the false positive where pyrefly incorrectly claimed that a string from an enum value wasn't assignable to a parameter expecting those exact literal values.
Attribution: The change to enum value type inference in pyrefly/lib/alt/class/enums.rs (specifically preserving value_ty instead of using the promoted type) fixed this false positive by making enum .value return unions of member literals

git-revise (-1)

The PR improved enum type inference to preserve literal types for enum member values. Before: Mode.GITLINK.value was inferred as bytes. After: it's inferred as Literal[b'160000']. This made the cast(bytes, entry.mode.value) no longer redundant since it's casting from a union of literals to the base type. The removed redundant-cast warning was a false positive - the cast serves a valid purpose of widening the type from literals to bytes.
Attribution: The changes to class_field.rs and enums.rs in the pyrefly PR modified enum member synthesis to preserve the original inferred value type (original_value_ty) instead of only using the promoted type. This caused Mode.value to have a union of literal types instead of just bytes, making the cast no longer redundant.

operator (-2)

The errors were false positives. The code passes rotate.value where rotate is a SecretRotate enum. With the PR's fix, pyrefly now correctly infers that SecretRotate.value returns one of the literal values ('daily', 'hourly', etc.) rather than just str, making it assignable to the parameter type Literal['daily', 'hourly', ...] | None. This is an improvement in type inference precision.
Attribution: The change to preserve original_value_ty in pyrefly/lib/alt/class/class_field.rs and pass it to check_enum_metadata allows enum .value lookups to recover unions of member literals instead of only their promoted base classes. This fixes the false positive where rotate.value (a string from an enum) couldn't be passed to a parameter expecting literal string values.

discord.py (+3, -33)

inconsistent-overload-default: These are new false positives. The overload implementation has a default value that matches the type annotation, but pyrefly is incorrectly complaining about a mismatch between Literal[AppCommandType.chat_input] in the overload and the implementation. This appears to be a pyrefly bug in how it compares enum literals in overload signatures.
bad-typed-dict-key: Fixed false positives. Pyrefly was incorrectly rejecting enum values as TypedDict keys because it saw them as base types (int) instead of the required literals.
bad-argument-type: Fixed false positives. Similar issue - enum values are now correctly seen as matching literal type parameters.
no-matching-overload: Fixed false positive. The overload resolution now works correctly with literal enum values.
bad-assignment: Fixed false positive. Assignment of enum value to literal union type now works correctly.

Overall: This is an improvement. The PR fixed pyrefly's handling of enum member values to preserve literal types, which allows proper type checking when these values are used in contexts expecting specific literals (like TypedDict keys). The 33 removed errors were false positives where pyrefly was incorrectly rejecting valid code. The 3 new errors are overly strict warnings about overload defaults that other type checkers don't flag.

Attribution: The change to preserve original enum member value types in pyrefly/lib/alt/class/enums.rs (specifically changing from ty to value_ty in the LitEnum construction) caused pyrefly to start tracking literal types for enum values instead of their promoted base types.

graphql-core (-1)

The removed error was a false positive. Before the PR, pyrefly inferred operation_type.value as generic str and couldn't verify it was a valid TypedDict key. After the PR's enum improvements, pyrefly correctly infers the literal string values from the enum, recognizing them as valid TypedDict keys. This is an improvement - pyrefly now understands the code that was always correct.
Attribution: The change to enum member synthesis in pyrefly/lib/alt/class/enums.rs (specifically using value_ty instead of promoted ty for the enum literal type) allows pyrefly to preserve literal types for enum values, enabling it to recognize that operation_type.value produces valid TypedDict keys.

pytest (-2)

improvement - Pyrefly was incorrectly inferring enum .value types as just the base type (e.g., str) instead of the specific literal values. The PR fixes this to match Python's actual behavior where EnumMember.value returns that member's specific value. The removed errors were false positives.
Attribution: The change in pyrefly/lib/alt/class/enums.rs that preserves original_value_ty and uses value_ty instead of the promoted ty for enum member types directly caused these errors to be fixed. Specifically, line 263 changing from ty: ty.clone() to ty: value_ty.clone() in the LitEnum construction.

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

Suggested fixes

Summary: The PR's preservation of literal types for enum members causes 22 pyrefly-only errors in rotki where enum values can't be passed to functions expecting literal unions.

1. In check_enum_metadata() in pyrefly/lib/alt/class/enums.rs, when creating the LitEnum at line 263, use a union type that includes both the literal value type AND the promoted enum type: instead of ty: value_ty.clone(), use ty: Type::union([value_ty.clone(), ty.clone()]). This allows enum members to match both literal unions and enum type parameters.

Files: pyrefly/lib/alt/class/enums.rs
Confidence: high
Affected projects: rotki, xarray-dataclasses, altair
Fixes: bad-argument-type, no-matching-overload, bad-assignment, bad-index, bad-return, unsupported-operation
This preserves the PR's goal of tracking literal values for .value access while maintaining compatibility when enum members are used as arguments. The union type allows SupportedBlockchain.BITCOIN to match both Literal[SupportedBlockchain.BITCOIN] and SupportedBlockchain parameter types. Expected outcome: eliminates all 22 errors in rotki, plus the errors in xarray-dataclasses and altair.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (2 heuristic, 9 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