Skip to content

Conversation

Viicos
Copy link
Member

@Viicos Viicos commented Jul 10, 2025

Change Summary

Fixes #12045.
Part of #11613.

Related issue number

Checklist

  • The pull request title is a good summary of the changes - it will be used in the changelog
  • Unit tests for the changes exist
  • Tests pass on CI
  • Documentation reflects the changes where applicable
  • My PR is ready to review, please add a comment including the phrase "please review" to assign reviewers

@github-actions github-actions bot added the relnotes-fix Used for bugfixes. label Jul 10, 2025
Copy link

cloudflare-workers-and-pages bot commented Jul 10, 2025

Deploying pydantic-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: b1e3452
Status: ✅  Deploy successful!
Preview URL: https://035322ad.pydantic-docs.pages.dev
Branch Preview URL: https://dataclass-field-fix.pydantic-docs.pages.dev

View logs

@Viicos Viicos force-pushed the dataclass-field-fix branch from a3f3148 to c0431fd Compare July 10, 2025 09:42
Copy link

codspeed-hq bot commented Jul 10, 2025

CodSpeed Performance Report

Merging #12051 will not alter performance

Comparing dataclass-field-fix (b1e3452) with main (3a7fe26)

Summary

✅ 46 untouched benchmarks

Copy link
Contributor

github-actions bot commented Jul 10, 2025

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  pydantic
  dataclasses.py
  pydantic/_internal
  _dataclasses.py
Project Total  

This report was generated by python-coverage-comment-action

@Viicos Viicos added the third-party-tests Add this label on a PR to trigger 3rd party tests label Jul 10, 2025
@Viicos Viicos closed this Jul 10, 2025
@Viicos Viicos reopened this Jul 10, 2025
@Viicos Viicos added the needs-blogpost-entry This PR needs to be documented in the release notes blog post label Jul 10, 2025
Copy link
Contributor

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Wow, powerful!

If there's no alternative, moving ahead with this seems the most reasonable option to preserve correct semantics.

It would be nice if we could localise the patching just to the under-construction type somehow. I posted an idea but no idea if it actually works.

Comment on lines +275 to +278
!!! note
This approach is far from ideal, and can probably be the source of unwanted side effects/race conditions.
The previous implemented approach was mutating the `__annotations__` dict of `cls`, which is no longer a
safe operation in Python 3.14+, and resulted in unexpected behavior with field ordering anyway.
Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed, scary. If this became a problem, I think we could do stuff like set the field to be a wrapper object which checks what thread it's on and only exhibits the patched behaviour on this thread, but even that is observable so why bother 😢

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I think if we ever get issues about this, we could wrap the mutation in a threading lock (similar to what I've initially tried to do in #11851). For this to be an issue, a user would have to dynamically create Pydantic dataclasses in a multi-threaded environment, and have them subclassing a stdlib dataclass with at least one field using the Pydantic's Field() function which is quite unlikely to happen.

new_dc_field.kw_only = True
if default.repr is not True:
new_dc_field.repr = default.repr
dc_fields[field_name] = new_dc_field
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder, rather than mutating the fields of the base objects, is there a world where we instead patch the class attributes of the type under construction? That might be significantly less far-reaching in potential conflict?

Copy link
Member Author

@Viicos Viicos Jul 17, 2025

Choose a reason for hiding this comment

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

The issue is that the dataclasses module is doing roughly:

fields: dict[str, Field] = {}

for b in cls.__mro__[-1:0:-1]:
    base_fields = getattr(b, '__dataclass_fields__', None)
    if base_fields is not None:
        for f_name, f in base_fields.items():
            fields[f_name] = f

# Then fetch the annotations of the class under construction and build fields for it

I thought about patching the whole __dataclass_fields__ attribute directly, without mutating the original dict, but I don't know which one is best. I think we can revisit if we ever get issues.

@Viicos Viicos force-pushed the dataclass-field-fix branch from 1588780 to c19f555 Compare July 17, 2025 05:58
@Viicos Viicos force-pushed the dataclass-field-fix branch from c19f555 to bceff23 Compare July 17, 2025 05:59
@Viicos
Copy link
Member Author

Viicos commented Jul 17, 2025

FastAPI failures are unrelated, we merged a PR that changed the JSON Schema for decimals.

Copy link
Contributor

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Discussion makes sense, lgtm 👍

@Viicos Viicos merged commit a59dab9 into main Jul 21, 2025
91 of 96 checks passed
@Viicos Viicos deleted the dataclass-field-fix branch July 21, 2025 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-blogpost-entry This PR needs to be documented in the release notes blog post relnotes-fix Used for bugfixes. third-party-tests Add this label on a PR to trigger 3rd party tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Dataclass field inconsistencies
2 participants