Skip to content
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

Add @frozen_after_init decorator to make some @dataclass uses safer with V2 #8431

Merged
merged 5 commits into from
Oct 9, 2019

Conversation

Eric-Arellano
Copy link
Contributor

@Eric-Arellano Eric-Arellano commented Oct 9, 2019

Problem

In #8423 (comment), we realized the need to be able to not always use frozen=True when using dataclasses for the V2 engine. Specifically, when we need a custom __init__(), we cannot have frozen=True because __init__() must be able to mutate the object's attributes.

However, we still want to keep the benefits of frozen=True as much as possible. After we create the object, we do not want to allow any additional modifications. This is important because the V2 engine uses the __hash__() of an object to determine its node ID - if the object mutates after creation, the ID will become invalid.

Solution

As suggested in #8423 (comment), introduce a new decorator @frozen_after_init that only allows setting attributes in the __init__() and then marks the object as frozen after.

This is done by setting a flag in the __init__() to mark that the object should there-on-out be frozen. Then, we modify __setattr__() to check for that flag to prohibit any mutations other than those in the __init__().

Result

@dataclass(unsafe_hash=True) can now be marked with @frozen_after_init so that we can have the custom __init__()s but not lose the benefits of frozen=True.

This also works on any arbitrary class.

We do not yet enforce that dataclasses must use this attribute (or frozen=True), which is left for #8181.

Copy link
Contributor

@cosmicexplorer cosmicexplorer left a comment

Choose a reason for hiding this comment

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

Thank you so much!!!

tests/python/pants_test/util/test_meta.py Outdated Show resolved Hide resolved
Eric-Arellano and others added 3 commits October 9, 2019 10:51
Co-Authored-By: Danny McClanahan <1305167+cosmicexplorer@users.noreply.github.com>
Copy link
Contributor

@benjyw benjyw left a comment

Choose a reason for hiding this comment

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

NEAT!

src/python/pants/util/meta.py Outdated Show resolved Hide resolved
@Eric-Arellano Eric-Arellano changed the title Add @freeze_after_init decorator to make some @dataclass uses safer with V2 Add @frozen_after_init decorator to make some @dataclass uses safer with V2 Oct 9, 2019
@Eric-Arellano Eric-Arellano merged commit d1efb1b into pantsbuild:master Oct 9, 2019
@Eric-Arellano Eric-Arellano deleted the freeze-after-init branch October 9, 2019 22:04
Eric-Arellano added a commit that referenced this pull request Oct 10, 2019
Builds off of #8362, this time tackling trickier instances.

This introduces a new idiom of using `@dataclass(unsafe_hash=True)`. Certain custom `__new__` implementations could not be implemented using either the normal dataclass auto-generated `__init__()` nor through `__post_init__()`. When `frozen=True`, it is impossible to implement your own `__init__()` because you cannot set attribute values. So, we cannot use `frozen=True` but still must have a hash to work with V2. `unsafe_hash=True` does this for us. We use `@frozen_after_init` from #8431 to ensure that we don't accidentally mutate the object after.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants