Skip to content

stdlib.weakref:finalize todo clarification #8437

Closed

Description

@kkirsche

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

Activity

  1. kkirsche commented on Jul 29, 2022

    @kkirsche
    ContributorAuthor

    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.

  2. srittau commented on Jul 29, 2022

    @srittau
    Collaborator

    Cc @sobolevn as author of that comment.

  3. sobolevn commented on Jul 29, 2022

    @sobolevn
    Member

    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 {})
  4. kkirsche commented on Aug 18, 2022

    @kkirsche
    ContributorAuthor

    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions