Skip to content

Inconsistent behavior of Timer.remove_callback. #12035

Open
@anntzer

Description

@anntzer

Currently, the docstrings of TimerBase.add_callback and remove_callback read,
respectively

        Register `func` to be called by timer when the event fires. Any
        additional arguments provided will be passed to `func`.

and

        Remove `func` from list of callbacks. `args` and `kwargs` are optional
        and used to distinguish between copies of the same function registered
        to be called with different arguments.

AFAICT, the implementations match the behavior, but importantly, if called with
no args/kwargs, then remove_callback removes add instances where the function
has been added regardless of the presence of args/kwargs (hinted at by the
docstring, though possibly not obvious).

This means that if you have

def f(x=0): ...
timer.add_callback(f)
timer.add_callback(f, 1)

then you cannot remove the first callback without removing the second callback.

Compare with

def f(x=0): ...
timer.add_callback(f, 0)
timer.add_callback(f, 1)

where you can.

What can be changed to make things more consistent? On the one hand one could
say that remove_callback only removes complete matches (f, args, kwargs), i.e.
remove_callback(f) would undo add_callback(f) but not add_callback(f, 1).
Alternatively, we can look into the stdlib's atexit.{register,unregister} which
needs to solve essentially the same semantics problem. There, the choice that
was made was for atexit.unregister to only take the function as argument, and
remove all instances of that function regardless of args/kwargs; if one wants
to distinguish between them for removal one can always pass in a partial(f,
...) object; note that different instances of partial objects always compare
inequal even when the constructor is called twice with the same parameters.
(This is made simpler by atexit.register returning its first argument (for use
as a decorator), so one can do p = atexit.register(partial(f, ...)) to later
atexit.unregister(p).

Metadata

Metadata

Assignees

No one assigned

    Labels

    keepItems to be ignored by the “Stale” Github Action

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions