Skip to content

[ty] Add support for dynamic dataclasses via make_dataclass#22586

Draft
charliermarsh wants to merge 1 commit intomainfrom
charlie/functional-dict
Draft

[ty] Add support for dynamic dataclasses via make_dataclass#22586
charliermarsh wants to merge 1 commit intomainfrom
charlie/functional-dict

Conversation

@charliermarsh
Copy link
Member

Summary

Like #22327, but for dataclasses.

@charliermarsh charliermarsh added the ty Multi-file analysis & type inference label Jan 14, 2026
@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 14, 2026

Typing conformance results

No changes detected ✅

@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 14, 2026

mypy_primer results

Changes were detected when running on open source projects
pydantic (https://github.com/pydantic/pydantic)
- pydantic/_internal/_core_metadata.py:87:54: error[invalid-assignment] Invalid assignment to key "pydantic_js_extra" with declared type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | ((dict[str, int | float | str | ... omitted 3 union elements], type[Any], /) -> None)` on TypedDict `CoreMetadata`: value of type `dict[object, object]`
+ pydantic/_internal/_core_metadata.py:87:54: error[invalid-assignment] Invalid assignment to key "pydantic_js_extra" with declared type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | ((dict[str, Divergent], type[Any], /) -> None)` on TypedDict `CoreMetadata`: value of type `dict[object, object]`
- pydantic/fields.py:949:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
+ pydantic/fields.py:949:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
- pydantic/fields.py:989:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
+ pydantic/fields.py:989:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
- pydantic/fields.py:1032:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
+ pydantic/fields.py:1032:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
- pydantic/fields.py:1072:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
+ pydantic/fields.py:1072:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
- pydantic/fields.py:1115:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
+ pydantic/fields.py:1115:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
- pydantic/fields.py:1154:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
+ pydantic/fields.py:1154:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
- pydantic/fields.py:1194:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`
+ pydantic/fields.py:1194:5: error[invalid-parameter-default] Default value of type `PydanticUndefinedType` is not assignable to annotated parameter type `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`
- pydantic/fields.py:1573:13: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, int | float | str | ... omitted 3 union elements] | ((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) | None`, found `Top[dict[Unknown, Unknown]] | (((dict[str, int | float | str | ... omitted 3 union elements], /) -> None) & ~Top[dict[Unknown, Unknown]]) | None`
+ pydantic/fields.py:1573:13: error[invalid-argument-type] Argument is incorrect: Expected `dict[str, Divergent] | ((dict[str, Divergent], /) -> None) | None`, found `Top[dict[Unknown, Unknown]] | (((dict[str, Divergent], /) -> None) & ~Top[dict[Unknown, Unknown]]) | None`

prefect (https://github.com/PrefectHQ/prefect)
- src/integrations/prefect-dbt/prefect_dbt/core/settings.py:94:28: error[invalid-assignment] Object of type `T@resolve_block_document_references | int | dict[str, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
+ src/integrations/prefect-dbt/prefect_dbt/core/settings.py:94:28: error[invalid-assignment] Object of type `dict[str, Any] | int | T@resolve_block_document_references | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/prefect/cli/deploy/_core.py:86:21: error[invalid-assignment] Object of type `T@resolve_block_document_references | int | dict[str, Any] | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
+ src/prefect/cli/deploy/_core.py:86:21: error[invalid-assignment] Object of type `dict[str, Any] | int | T@resolve_block_document_references | ... omitted 4 union elements` is not assignable to `dict[str, Any]`
- src/prefect/deployments/steps/core.py:137:38: error[invalid-argument-type] Argument is incorrect: Expected `T@resolve_variables`, found `T@resolve_block_document_references | int | dict[str, Any] | ... omitted 4 union elements`
+ src/prefect/deployments/steps/core.py:137:38: error[invalid-argument-type] Argument is incorrect: Expected `T@resolve_variables`, found `dict[str, Any] | int | T@resolve_block_document_references | ... omitted 4 union elements`
- src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `T@resolve_block_document_references | int | dict[str, Any] | ... omitted 4 union elements` on object of type `dict[str, Any]`
+ src/prefect/utilities/templating.py:320:13: error[invalid-assignment] Invalid subscript assignment with key of type `object` and value of type `dict[str, Any] | int | T@resolve_block_document_references | ... omitted 4 union elements` on object of type `dict[str, Any]`
- src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | T@resolve_block_document_references | int | ... omitted 5 union elements]`
+ src/prefect/utilities/templating.py:323:16: error[invalid-return-type] Return type does not match returned value: expected `T@resolve_block_document_references | dict[str, Any]`, found `list[Unknown | dict[str, Any] | int | ... omitted 5 union elements]`
- src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Expected `T@resolve_variables`, found `T@resolve_block_document_references | int | dict[str, Any] | ... omitted 4 union elements`
+ src/prefect/workers/base.py:232:13: error[invalid-argument-type] Argument is incorrect: Expected `T@resolve_variables`, found `dict[str, Any] | int | T@resolve_block_document_references | ... omitted 4 union elements`

strawberry (https://github.com/strawberry-graphql/strawberry)
+ strawberry/experimental/pydantic/error_type.py:149:37: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
+ strawberry/experimental/pydantic/object_type.py:255:24: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
- strawberry/experimental/pydantic/object_type.py:271:9: error[unresolved-attribute] Unresolved attribute `_pydantic_type` on type `type`
- strawberry/experimental/pydantic/object_type.py:293:13: error[unresolved-attribute] Unresolved attribute `from_pydantic` on type `type`
- strawberry/experimental/pydantic/object_type.py:295:13: error[unresolved-attribute] Unresolved attribute `to_pydantic` on type `type`
- Found 345 diagnostics
+ Found 344 diagnostics

static-frame (https://github.com/static-frame/static-frame)
- static_frame/core/bus.py:675:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Bus[Any], object_]`, found `InterGetItemILocReduces[Self@iloc | Bus[Any], object_ | Self@iloc]`
+ static_frame/core/bus.py:671:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemLocReduces[Bus[Any], object_]`, found `InterGetItemLocReduces[Bus[Any] | Bottom[Series[Any, Any]] | TypeBlocks | ... omitted 6 union elements, object_]`
+ static_frame/core/bus.py:675:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Bus[Any], object_]`, found `InterGetItemILocReduces[Bus[Any] | Bottom[Index[Any]] | Bottom[Series[Any, Any]] | ... omitted 7 union elements, object_ | Self@iloc]`
+ static_frame/core/series.py:772:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Series[Any, Any], TVDtype@Series]`, found `InterGetItemILocReduces[Series[Any, Any] | IndexHierarchy | TypeBlocks | ... omitted 7 union elements, TVDtype@Series]`
+ static_frame/core/series.py:4072:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[SeriesHE[Any, Any], TVDtype@SeriesHE]`, found `InterGetItemILocReduces[Bottom[Series[Any, Any]] | Bottom[Index[Any]] | TypeBlocks | ... omitted 8 union elements, TVDtype@SeriesHE]`
+ static_frame/core/yarn.py:418:16: error[invalid-return-type] Return type does not match returned value: expected `InterGetItemILocReduces[Yarn[Any], object_]`, found `InterGetItemILocReduces[Yarn[Any] | Bottom[Index[Any]] | Bottom[Series[Any, Any]] | ... omitted 7 union elements, object_]`
- Found 1821 diagnostics
+ Found 1825 diagnostics

rotki (https://github.com/rotki/rotki)
+ rotkehlchen/chain/decoding/tools.py:96:44: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
- rotkehlchen/chain/decoding/tools.py:97:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `BTCAddress | ChecksumAddress | SubstrateAddress | SolanaAddress`, found `A@BaseDecoderTools`
+ rotkehlchen/chain/decoding/tools.py:99:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `Sequence[A@BaseDecoderTools]`, found `Unknown | tuple[BTCAddress, ...] | tuple[ChecksumAddress, ...] | tuple[SubstrateAddress, ...] | tuple[SolanaAddress, ...]`
- rotkehlchen/chain/decoding/tools.py:98:13: error[invalid-argument-type] Argument to function `decode_transfer_direction` is incorrect: Expected `BTCAddress | ChecksumAddress | SubstrateAddress | SolanaAddress | None`, found `A@BaseDecoderTools | None`
+ rotkehlchen/chain/decoding/tools.py:100:62: warning[unused-ignore-comment] Unused blanket `type: ignore` directive
- Found 2049 diagnostics
+ Found 2050 diagnostics

No memory usage changes detected ✅

@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 2 times, most recently from 0c2c95b to c9c3caa Compare January 14, 2026 22:36
@charliermarsh charliermarsh marked this pull request as ready for review January 15, 2026 02:58
@charliermarsh charliermarsh marked this pull request as draft January 15, 2026 02:59
@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 2 times, most recently from 71f3816 to 929be6b Compare January 15, 2026 03:22
@charliermarsh charliermarsh marked this pull request as ready for review January 15, 2026 03:22
@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 2 times, most recently from 37f23f2 to 0bcbb7e Compare January 15, 2026 03:59
@astral-sh-bot
Copy link

astral-sh-bot bot commented Jan 15, 2026

ecosystem-analyzer results

Lint rule Added Removed Changed
invalid-return-type 1 4 4
invalid-argument-type 1 2 3
invalid-assignment 0 0 5
unused-ignore-comment 4 0 0
unresolved-attribute 0 3 0
Total 6 9 12

Full report with detailed diff (timing results)

@AlexWaygood AlexWaygood reopened this Jan 15, 2026
@AlexWaygood
Copy link
Member

AlexWaygood commented Jan 15, 2026

I'm a bit hesitant about this one. The typing spec doesn't say anything about this function AFAIK, and I don't remember seeing any user requests on the issue tracker for us to add special-cased support. Mypy_primer only shows two diagnostics going away in the ecosystem, on strawberry -- and it looks like those lines already have type: ignores on them for mypy.

Meanwhile, this is quite a bit of new code, and the manual parsing of arguments in infer/builder.rs is quite complicated and error-prone.

Do any other type checkers have special-cased support for this function? We do have to draw the line somewhere.

I think I'd feel differently if we could share more of the call-expression-parsing logic with what we've already added for namedtuples (which definitely was necessary), but the schema make_dataclass() expects is just different enough that sharing more of the code seems like it could be pretty difficult?

Another -- much simpler -- way to avoid false positives with the objects returned by this function would be to just special-case the return type so that we infer type[Unknown] rather than type -- that wouldn't involve any custom call-expression parsing.

@charliermarsh
Copy link
Member Author

AFAICT, Mypy and Pyright do not support it. But... I would still advocate for us to support it? We have all the infrastructure to do so! And almost all of the new code is in argument parsing -- which is very verbose, but at least fairly mechanical and testable? (E.g., it's much easier to know when we have that right, as opposed to something deep in type inference.) I think I don't agree that this is where we should draw the line, unless it's a feature that we can't support for reasons that I don't yet understand or would be uncovered by your review.

@AlexWaygood
Copy link
Member

(I'm curious for @carljm's and @dcreager's opinions!)

@charliermarsh
Copy link
Member Author

Ditto and am totally fine to be overruled on it of course. I took it as a given that we'd support this given that it's in the type system overview; that may have been a mistake!

@AlexWaygood
Copy link
Member

Ditto and am totally fine to be overruled on it of course. I took it as a given that we'd support this given that it's in the type system overview; that may have been a mistake!

Well, that issue just says that we don't have any tests for the function right now. But this PR adds a lot more than just test coverage 😆

}

/// Look up an instance member by name (including superclasses).
pub(crate) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
Copy link
Member

Choose a reason for hiding this comment

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

It seems unfortunate that we have to repeat all those methods for every dynamic class literal. Can't we share more infrastructure?

@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 2 times, most recently from b968cdf to b66f74a Compare January 15, 2026 20:38
@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 3 times, most recently from 07207c7 to 6e36caa Compare January 16, 2026 14:59
@charliermarsh charliermarsh marked this pull request as ready for review January 16, 2026 14:59
@charliermarsh
Copy link
Member Author

Can probably hold off on further review until #22627 is resolved, since it will have implications for this PR.

@charliermarsh charliermarsh marked this pull request as draft January 19, 2026 13:41
@charliermarsh
Copy link
Member Author

(Marking as draft while we finish the recursive definition work upstream.)

@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 2 times, most recently from ccabd74 to 16f4349 Compare January 21, 2026 23:16
@carljm carljm removed their request for review January 21, 2026 23:47
@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 7 times, most recently from dabdc49 to 74ec01d Compare January 22, 2026 03:22
@charliermarsh charliermarsh marked this pull request as ready for review January 22, 2026 03:22
@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch from 74ec01d to 239ac3a Compare January 22, 2026 03:52
@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch 2 times, most recently from c41d60b to c7ab85f Compare January 22, 2026 16:18
@charliermarsh charliermarsh marked this pull request as draft January 22, 2026 18:20
@charliermarsh charliermarsh force-pushed the charlie/functional-dict branch from c7ab85f to 3e78ed6 Compare January 22, 2026 19:14
@charliermarsh charliermarsh marked this pull request as ready for review January 22, 2026 19:16
@charliermarsh charliermarsh marked this pull request as draft January 22, 2026 19:27
@charliermarsh
Copy link
Member Author

I'm going to mark as draft because I think we should merge #22792 first.

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

Labels

ecosystem-analyzer ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants