Skip to content

opt_frozen_dataclass: Enable hashing with -O - #273

Merged
inducer merged 2 commits into
mainfrom
opt-frozen-hashable
Dec 4, 2024
Merged

opt_frozen_dataclass: Enable hashing with -O#273
inducer merged 2 commits into
mainfrom
opt-frozen-hashable

Conversation

@inducer

@inducer inducer commented Dec 2, 2024

Copy link
Copy Markdown
Owner

Inspired by inducer/pytato#563.

cc @matthiasdiener (what do you think?)

@matthiasdiener

Copy link
Copy Markdown
Contributor

Dunno, seems risky 😬

@matthiasdiener

Copy link
Copy Markdown
Contributor

Maybe replacing unsafe_hash: bool = False, with unsafe_hash: bool = None, could be an option?

@inducer

inducer commented Dec 3, 2024

Copy link
Copy Markdown
Owner Author

Dunno, seems risky 😬

I'd argue it's needed for correctness. Otherwise, these classes are hashable when running without -O and not hashable when running with -O, which is inconsistent. They're supposed to be semantically frozen classes, as the name suggests. I'll add a comment as to why that's there.

@inducer

inducer commented Dec 3, 2024

Copy link
Copy Markdown
Owner Author

Maybe replacing unsafe_hash: bool = False, with unsafe_hash: bool = None, could be an option?

Sure, I could see that.

@matthiasdiener

matthiasdiener commented Dec 3, 2024

Copy link
Copy Markdown
Contributor

Dunno, seems risky 😬

I'd argue it's needed for correctness. Otherwise, these classes are hashable when running without -O and not hashable when running with -O, which is inconsistent. They're supposed to be semantically frozen classes, as the name suggests. I'll add a comment as to why that's there.

Part of the problem is that setting unsafe_hash=True disallows a custom hash function:

from dataclasses import dataclass

@dataclass(unsafe_hash=True)
class A:
    x: int

    def __hash__(self):
        return 42
$ python dc.py
Traceback (most recent call last):
  File "/Users/mdiener/Work/emirge/mirgecom/dc.py", line 4, in <module>
    @dataclass(unsafe_hash=True)
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1222, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 1091, in _process_class
    cls.__hash__ = hash_action(cls, field_list, globals)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/mdiener/Work/emirge/miniforge3/envs/ceesd/lib/python3.11/dataclasses.py", line 851, in _hash_exception
    raise TypeError(f'Cannot overwrite attribute __hash__ '
TypeError: Cannot overwrite attribute __hash__ in class A

This error does not happen if just setting frozen=True, and I think this might be confusing to users of opt_frozen_dataclass.

@inducer
inducer force-pushed the opt-frozen-hashable branch 2 times, most recently from 16d62ed to 9c936f8 Compare December 3, 2024 16:27
@inducer

inducer commented Dec 3, 2024

Copy link
Copy Markdown
Owner Author

Part of the problem is that setting unsafe_hash=True disallows a custom hash function:

Maybe that's playing whack-a-mole, but the latest revision addresses this.

@inducer
inducer force-pushed the opt-frozen-hashable branch from 9c936f8 to 1226a0c Compare December 3, 2024 16:31
@inducer

inducer commented Dec 3, 2024

Copy link
Copy Markdown
Owner Author

Paging @alexfikl for a third set of eyes: Is all this just a terrible idea and not worth it?

Comment thread pytools/__init__.py Outdated
Comment thread pytools/__init__.py Outdated
Comment thread pytools/__init__.py Outdated
@inducer
inducer force-pushed the opt-frozen-hashable branch from 1226a0c to f20e04a Compare December 3, 2024 16:49
@matthiasdiener

Copy link
Copy Markdown
Contributor

Side note: this table is really helpful when thinking through this.

Comment thread pytools/__init__.py Outdated
@@ -3000,16 +3000,41 @@ def opt_frozen_dataclass(
this decorator avoid when the interpreter runs with "optimization"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
this decorator avoid when the interpreter runs with "optimization"
this decorator avoids when the interpreter runs with "optimization"

Comment thread pytools/__init__.py Outdated
Comment on lines +3003 to +3005
The resulting dataclass supports hashing unless *eq* is set to *False*,
if *unsafe_hash* is left at the default or set to *True*.

@matthiasdiener matthiasdiener Dec 3, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
The resulting dataclass supports hashing unless *eq* is set to *False*,
if *unsafe_hash* is left at the default or set to *True*.
The resulting dataclass supports hashing, even when it is not actually frozen,
if *unsafe_hash* is left at the default or set to *True*.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also, this is still hashable with eq=False, like normal dataclasses.

@matthiasdiener matthiasdiener left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Besides the minor wording comments, this LGTM. Thanks!

@alexfikl

alexfikl commented Dec 3, 2024

Copy link
Copy Markdown
Contributor

Paging @alexfikl for a third set of eyes: Is all this just a terrible idea and not worth it?

Hm, I haven't followed this much, but if I understand correctly.. we want to turn off frozen when in -O mode because it's slower, but that also turns off hashing by default, right? And this is meant to sneakily add back hashing as if the class was frozen?

Assuming that's the intention, that sounds reasonable to me. The code looks fine now with @matthiasdiener's suggestions 😁

Co-authored-by: Matthias Diener <mdiener@illinois.edu>
@inducer
inducer force-pushed the opt-frozen-hashable branch from cb2683a to d22dec6 Compare December 4, 2024 17:47
@inducer
inducer force-pushed the opt-frozen-hashable branch from d22dec6 to 7b4d68a Compare December 4, 2024 17:52
@inducer
inducer enabled auto-merge (rebase) December 4, 2024 17:54
@inducer

inducer commented Dec 4, 2024

Copy link
Copy Markdown
Owner Author

Thanks all for your help in getting this into shape. I'll do another release as soon as this is in.

@inducer
inducer merged commit dcfbda7 into main Dec 4, 2024
@inducer
inducer deleted the opt-frozen-hashable branch December 4, 2024 18:01
@inducer

inducer commented Dec 4, 2024

Copy link
Copy Markdown
Owner Author

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