Skip to content

Delay instance faux-immutablity until after model_post_init completes. #11495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
5 of 13 tasks
gregglind opened this issue Feb 26, 2025 · 3 comments
Open
5 of 13 tasks

Comments

@gregglind
Copy link

Initial Checks

  • I have searched Google & GitHub for similar requests and couldn't find anything
  • I have read and followed the docs and still think this feature is missing

Description

Goal:

  • faux-immutable instances that still allows for destructive modification during post_model_init

Case

As a data scientist, I often create classes that have very complex, multi-field validation and inference and imputation using the entire object.

I currently use model_post_init to "clean up" and impute missing fields.

Alternatives considered:

  1. Init and model_post_init using an unfrozen class, that feeds the results into the frozen class.

    https://gist.github.com/gregglind/0ee7074a1642dfc485564f5bdf245d59

  2. Code habit changes to avoid this creating such complex objects.

Blockers

  1. Model config is on the class, not the instance.
  2. Attribute 'freezing' is set before model_post_init

Prior art

Others have raised related issues about 'frozen':

Affected Components

@Viicos
Copy link
Member

Viicos commented Apr 8, 2025

I'm kind of surprised no one has reported an issue on the CPython issue tracker about this for stdlib dataclasses (which suffer from the same issue).

I can see how this can be useful, although I think it makes sense to behave as it does today by definition: the post init logic happens after initialization, meaning the model should be frozen. It might be best to require the user to explicitly "unfreeze" the model, e.g. with a context manager:

class Model(BaseModel, frozen=True):
    def model_post_init(self, context):
        with self.with_config(frozen=False):
            # Apply mutations

This API has the benefit of being extendable to other configuration values, e.g. validate_assignment, etc (and came up already in #10814 (reply in thread)).

Doing this for frozen would be straightforward as the check is performed in Python (in BaseModel.__setattr__). Doing this for other configuration values might be harder, as it would require work in pydantic core.

Wdyt @davidhewitt?

@davidhewitt
Copy link
Contributor

Hmm, interesting.

the post init logic happens after initialization, meaning the model should be frozen

I agree with this. I think that as well as dataclasses, attrs also behaves the same way. I think we should not be changing the timing of the immutability as proposed in this PR, I think it's also potentially a breaking behaviour change for many users that we don't really need to commit too.

The API to temporarily unfreeze - or modify other config - yes, that sound reasonable. I think in general in pydantic-core we currently bake too much config directly into the validator structure, I guess again this is a case of pydantic/pydantic-core#1337

@Viicos
Copy link
Member

Viicos commented Apr 11, 2025

It might be best to require the user to explicitly "unfreeze" the model, e.g. with a context manager:

Discussed with the team, we prefer not going this way for now. Trying to bypass frozen/validate_assignment isn't an usual thing and contradicts the contract set on the class by the user, so we prefer to recommend to using object.__setattr__() or modifying the instance's __dict__ directly.

I'm leaving the feature request open as it isn't directly related, and we might allow such mutations in model_post_init() in the future.

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

No branches or pull requests

3 participants