Skip to content

stdlib.weakref:finalize todo clarification #8437

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

Closed
kkirsche opened this issue Jul 29, 2022 · 4 comments
Closed

stdlib.weakref:finalize todo clarification #8437

kkirsche opened this issue Jul 29, 2022 · 4 comments

Comments

@kkirsche
Copy link
Contributor

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

@kkirsche
Copy link
Contributor Author

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.

@srittau
Copy link
Collaborator

srittau commented Jul 29, 2022

Cc @sobolevn as author of that comment.

@sobolevn
Copy link
Member

sobolevn commented Jul 29, 2022

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 {})

@kkirsche
Copy link
Contributor Author

Closing as I fundamentally disagree with the suggested fix of simply removing the todo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants