-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
Regression in Django with singledispatchmethod on models #127750
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
Comments
I see what the issue is now: https://github.com/AlexWaygood/cpython/blob/3d8d4ba5f5367f2d87488b00afd55f75401c2b7d/Lib/functools.py#L950 Since dictionaries use from functools import singledispatchmethod
from dataclasses import dataclass
@dataclass(frozen=True) # or (unsafe_hash=True)
class Test:
pk: int
@singledispatchmethod
def broken(self, x):
raise NotImplemenetedError
@broken.register
def _(self, x: int):
return id(self)
t1 = Test(1)
print(id(t1))
t2 = Test(1)
print(id(t2))
assert id(t1) == t1.broken(2) # both t1 and t2 will share the same dispatcher
assert id(t2) == t2.broken(2) # fails! |
I have a fix, I think. Working on a PR. |
I'm pretty sure the code in question is also leaking memory. See comment here. |
@vodik There is indeed a behavior change between 3.12 and 3.13, so perhaps that needs to be fixed. Another example showing the issue is: from functools import lru_cache
from dataclasses import dataclass
@dataclass(frozen=True) # or (unsafe_hash=True)
class Test:
pk: int
t1 = Test(1)
t2 = Test(1)
@lru_cache
def return_id(obj):
return id(obj)
print(return_id(t1))
print(return_id(t2)) |
I didn't think about that, but yeah, I guess yeah you'd see the same behaviour here. Though there would be an expectation that a dictionary is involved with an Funny this is actually documented behaviour on the Footnotes |
Remove broken singledispatchmethod caching introduced in pythongh-85160. Achieve the same performance using different optimization.
The proposed solutions #127839 and #128648 still have issues with reference loops. They also do not work for all objects (immutable or unhashable). Every solution that involves caching will have such issues. But caching is not needed for the original issue. Creating a new function is not slow, its updating multiple attributes which will never be used is slow. #130008 proposes an alternative solution. Instead of caching, it delays request of attributes. The resulting performance of calling the singledispatchmethod method is the same. I think that that changes should be reverted in 3.13. There may be issues with the #130008 solution (not such severe like in the current code, but people can use it in a weird way). We should not risk. |
The reference loops in #128648 might be ok from a performance point of view (but to be honest, I am unsure what kind of benchmarks we should run to prove or disprove this). The approach from @serhiy-storchaka seems better though. I agree with reverting the changes in 3.13, I will make a PR later. |
…ethod The code is still flawed, because it does not recognize class and static methods, and the first argument is not removed from the signature of bound methods, but at least it does not worse than in 3.13 and older.
…H-130309) The code is still flawed, because it does not recognize class and static methods, and the first argument is not removed from the signature of bound methods, but at least it does not worse than in 3.13 and older.
…spatchmethod (pythonGH-130309) The code is still flawed, because it does not recognize class and static methods, and the first argument is not removed from the signature of bound methods, but at least it does not worse than in 3.13 and older. (cherry picked from commit 10b3205) Co-authored-by: Serhiy Storchaka <[email protected]>
…ythonGH-130309) (cherry picked from commit 395335d) (cherry picked from commit 10b3205)
…ythonGH-130309) (pythonGH-130340) (cherry picked from commit 68c57d6) Co-authored-by: Serhiy Storchaka <[email protected]> (cherry picked from commit 395335d) (cherry picked from commit 10b3205)
Uh oh!
There was an error while loading. Please reload this page.
Bug report
Bug description:
Also reported upstream to Django here.
The following code works on Python 3.12 and I believe #85160 is the cause.
When traversing a relationship and then calling a
singledispatchmethod
method registered on a model, the reference to self seems to get cached and locked to the first created model.and then it's pretty easy to trigger:
CPython versions tested on:
3.13
Operating systems tested on:
macOS
Linked PRs
The text was updated successfully, but these errors were encountered: