typeshed
typeshed copied to clipboard
`stdlib.weakref:finalize` todo clarification
In weakref.pyi, there is the following todo:
https://github.com/python/typeshed/blob/master/stdlib/weakref.pyi#L127
class finalize: # TODO: This is a good candidate for to be a `Generic[_P, _T]` class
def __init__(self, __obj: object, __func: Callable[_P, Any], *args: _P.args, **kwargs: _P.kwargs) -> None: ...
def __call__(self, _: Any = ...) -> Any | None: ...
def detach(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ...
def peek(self) -> tuple[Any, Any, tuple[Any, ...], dict[str, Any]] | None: ...
@property
def alive(self) -> bool: ...
atexit: bool
Looking at the documentation for finalize, seen here I can see that:
finalize provides a straight forward way to register a cleanup function to be called when an object is garbage collected. This is simpler to use than setting up a callback function on a raw weak reference, since the module automatically ensures that the finalizer remains alive until the object is collected.
And is constructed with the object, callable, etc. The implementation is seen here: https://github.com/python/cpython/blob/main/Lib/weakref.py#L540
I was wondering if this could be clarified to help me understand what the correct way to implement that was.
As I understand the docs of finalize though, I think this would be:
class finalize(Generic[_P, _T, _R]): # _P paramspec, _T for the object, _R for the return value of the callable
atexit: bool
def __init__(self, __obj: _T, __func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> None: ...
def __call__(self, _: object _T = ...) -> _R | None: ... # return the result of P, change Any to object as it's unused
def detach(self) -> tuple[_T, Callable[_P, _R], _P.args, _P.kwargs] | None: ...
def peek(self) -> tuple[_T, Callable[_P, _R], _P.args, _P.kwargs] | None: ...
@property
def alive(self) -> bool: ...
Any tips would be appreciated
Note, the use of _P.args in the tuple I believe is not allowed, though it's how as a user I had originally understood and expected ParamSpec to work. Similar to how I expected it to be able to be bound to a function so that you could re-use a parameter specification of an existing function in other functions, essentially always keeping them up to date.
Cc @sobolevn as author of that comment.
Yes, I was thinking about typing methods like detach:
def detach(self):
"""If alive then mark as dead and return (obj, func, args, kwargs);
otherwise return None"""
info = self._registry.get(self)
obj = info and info.weakref()
if obj is not None and self._registry.pop(self, None):
return (obj, info.func, info.args, info.kwargs or {})
Closing as I fundamentally disagree with the suggested fix of simply removing the todo.