FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-127750: Improve caching in singledispatchmethod by vodik · Pull Request #127751 · python/cpython · GitHub

Repository navigation

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (1) .rst  (1) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
52 changes: 34 additions & 18 deletions Lib/functools.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1027,9 +1027,7 @@ def __init__(self, func):

self.dispatcher = singledispatch(func)
self.func = func

import weakref # see comment in singledispatch function
self._method_cache = weakref.WeakKeyDictionary()
self._method_cache = {}

def register(self, cls, method=None):
"""generic_method.register(cls, func) -> func
Expand All @@ -1039,30 +1037,48 @@ def register(self, cls, method=None):
return self.dispatcher.register(cls, func=method)

def __get__(self, obj, cls=None):
if self._method_cache is not None:
try:
_method = self._method_cache[obj]
except TypeError:
self._method_cache = None
except KeyError:
pass
else:
cache_key = id(obj)

try:
_obj_ref, _method = self._method_cache[id(obj)]
except KeyError:
pass
else:
if _obj_ref() is obj:
return _method

dispatch = self.dispatcher.dispatch
funcname = getattr(self.func, '__name__', 'singledispatchmethod method')
def _method(*args, **kwargs):
if not args:
raise TypeError(f'{funcname} requires at least '
'1 positional argument')
return dispatch(args[0].__class__).__get__(obj, cls)(*args, **kwargs)

def _remove(_):
self._method_cache.pop(cache_key, None)

import weakref # see comment in singledispatch function
try:
obj_ref = weakref.ref(obj, _remove)
except TypeError:
cache_key = None

def _method(*args, **kwargs):
if not args:
raise TypeError(f'{funcname} requires at least '
'1 positional argument')
return dispatch(args[0].__class__).__get__(obj, cls)(*args, **kwargs)
else:
def _method(*args, **kwargs):
if not args:
raise TypeError(f'{funcname} requires at least '
'1 positional argument')
# Make sure to use the weakref here to prevent storing
# a strong reference to obj in the cache
return dispatch(args[0].__class__).__get__(obj_ref(), cls)(*args, **kwargs)

vodik Dec 9, 2024 •
edited
Loading

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Doesn't this introduce a data race though? If I assigned the method and then deleted the underlying obj? I'll have to try it.

Is a weakref even appropriate for this cache then? Or should I throw a ReferenceError?

-                return dispatch(args[0].__class__).__get__(obj_ref(), cls)(*args, **kwargs)
+                obj = obj_ref()
+                if obj is None:
+                    raise ReferenceError
+
+                return dispatch(args[0].__class__).__get__(obj, cls)(*args, **kwargs)

Or is this whole approach maybe not correct enough?


_method.__isabstractmethod__ = self.__isabstractmethod__
_method.register = self.register
update_wrapper(_method, self.func)

if self._method_cache is not None:
self._method_cache[obj] = _method
if cache_key:
self._method_cache[cache_key] = (obj_ref, _method)

return _method

Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve caching of :class:`functools.singledispatchmethod` to fix issues
where sometimes different object instances could collide with each other.

Back | FazBrowse Home | New Git URL