Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #86016 > unrolled thread

Re: Design thought for callbacks

Started byChris Angelico <rosuav@gmail.com>
First post2015-02-21 16:42 +1100
Last post2015-02-22 15:54 +0200
Articles 6 — 3 participants

Back to article view | Back to comp.lang.python

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: Design thought for callbacks Chris Angelico <rosuav@gmail.com> - 2015-02-21 16:42 +1100
    Re: Design thought for callbacks Marko Rauhamaa <marko@pacujo.net> - 2015-02-21 18:03 +0200
      Re: Design thought for callbacks Cem Karan <cfkaran2@gmail.com> - 2015-02-22 07:10 -0500
        Re: Design thought for callbacks Marko Rauhamaa <marko@pacujo.net> - 2015-02-22 14:12 +0200
          Re: Design thought for callbacks Cem Karan <cfkaran2@gmail.com> - 2015-02-22 08:42 -0500
            Re: Design thought for callbacks Marko Rauhamaa <marko@pacujo.net> - 2015-02-22 15:54 +0200

#86016 — Re: Design thought for callbacks

FromChris Angelico <rosuav@gmail.com>
Date2015-02-21 16:42 +1100
SubjectRe: Design thought for callbacks
Message-ID<mailman.18948.1424497381.18130.python-list@python.org>
On Sat, Feb 21, 2015 at 1:44 PM, Cem Karan <cfkaran2@gmail.com> wrote:
> In order to inform users that certain bits of state have changed, I require them to register a callback with my code.  The problem is that when I store these callbacks, it naturally creates a strong reference to the objects, which means that if they are deleted without unregistering themselves first, my code will keep the callbacks alive.  Since this could lead to really weird and nasty situations, I would like to store all the callbacks in a WeakSet (https://docs.python.org/3/library/weakref.html#weakref.WeakSet).  That way, my code isn't the reason why the objects are kept alive, and if they are no longer alive, they are automatically removed from the WeakSet, preventing me from accidentally calling them when they are dead.  My question is simple; is this a good design?  If not, why not?  Are there any potential 'gotchas' I should be worried about?
>

No, it's not. I would advise using strong references - if the callback
is a closure, for instance, you need to hang onto it, because there
are unlikely to be any other references to it. If I register a
callback with you, I expect it to be called; I expect, in fact, that
that *will* keep my object alive.

ChrisA

[toc] | [next] | [standalone]


#86052

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-02-21 18:03 +0200
Message-ID<87zj87i74p.fsf@elektro.pacujo.net>
In reply to#86016
Chris Angelico <rosuav@gmail.com>:

> On Sat, Feb 21, 2015 at 1:44 PM, Cem Karan <cfkaran2@gmail.com> wrote:

>> In order to inform users that certain bits of state have changed, I
>> require them to register a callback with my code. The problem is that
>> when I store these callbacks, it naturally creates a strong reference
>> to the objects, which means that if they are deleted without
>> unregistering themselves first, my code will keep the callbacks
>> alive. Since this could lead to really weird and nasty situations,
>> [...]
>
> No, it's not. I would advise using strong references - if the callback
> is a closure, for instance, you need to hang onto it, because there
> are unlikely to be any other references to it. If I register a
> callback with you, I expect it to be called; I expect, in fact, that
> that *will* keep my object alive.

I use callbacks all the time but haven't had any problems with strong
references.

I am careful to move my objects to a zombie state after they're done so
they can absorb any potential loose callbacks that are lingering in the
system.


Marko

[toc] | [prev] | [next] | [standalone]


#86101

FromCem Karan <cfkaran2@gmail.com>
Date2015-02-22 07:10 -0500
Message-ID<mailman.19002.1424607035.18130.python-list@python.org>
In reply to#86052
On Feb 21, 2015, at 11:03 AM, Marko Rauhamaa <marko@pacujo.net> wrote:

> Chris Angelico <rosuav@gmail.com>:
> 
>> On Sat, Feb 21, 2015 at 1:44 PM, Cem Karan <cfkaran2@gmail.com> wrote:
> 
>>> In order to inform users that certain bits of state have changed, I
>>> require them to register a callback with my code. The problem is that
>>> when I store these callbacks, it naturally creates a strong reference
>>> to the objects, which means that if they are deleted without
>>> unregistering themselves first, my code will keep the callbacks
>>> alive. Since this could lead to really weird and nasty situations,
>>> [...]
>> 
>> No, it's not. I would advise using strong references - if the callback
>> is a closure, for instance, you need to hang onto it, because there
>> are unlikely to be any other references to it. If I register a
>> callback with you, I expect it to be called; I expect, in fact, that
>> that *will* keep my object alive.
> 
> I use callbacks all the time but haven't had any problems with strong
> references.
> 
> I am careful to move my objects to a zombie state after they're done so
> they can absorb any potential loose callbacks that are lingering in the
> system.

So, if I were designing a library for you, you would be willing to have a 'zombie' attribute on your callback, correct?  This would allow the library to query its callbacks to ensure that only 'live' callbacks are called.  How would you handle closures?  

Thanks,
Cem Karan

[toc] | [prev] | [next] | [standalone]


#86102

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-02-22 14:12 +0200
Message-ID<87k2za9mbf.fsf@elektro.pacujo.net>
In reply to#86101
Cem Karan <cfkaran2@gmail.com>:

> On Feb 21, 2015, at 11:03 AM, Marko Rauhamaa <marko@pacujo.net> wrote:
>> I use callbacks all the time but haven't had any problems with strong
>> references.
>> 
>> I am careful to move my objects to a zombie state after they're done so
>> they can absorb any potential loose callbacks that are lingering in the
>> system.
>
> So, if I were designing a library for you, you would be willing to have
> a 'zombie' attribute on your callback, correct? This would allow the
> library to query its callbacks to ensure that only 'live' callbacks are
> called. How would you handle closures?

Sorry, don't understand the question.


Marko

[toc] | [prev] | [next] | [standalone]


#86113

FromCem Karan <cfkaran2@gmail.com>
Date2015-02-22 08:42 -0500
Message-ID<mailman.19010.1424612539.18130.python-list@python.org>
In reply to#86102
On Feb 22, 2015, at 7:12 AM, Marko Rauhamaa <marko@pacujo.net> wrote:

> Cem Karan <cfkaran2@gmail.com>:
> 
>> On Feb 21, 2015, at 11:03 AM, Marko Rauhamaa <marko@pacujo.net> wrote:
>>> I use callbacks all the time but haven't had any problems with strong
>>> references.
>>> 
>>> I am careful to move my objects to a zombie state after they're done so
>>> they can absorb any potential loose callbacks that are lingering in the
>>> system.
>> 
>> So, if I were designing a library for you, you would be willing to have
>> a 'zombie' attribute on your callback, correct? This would allow the
>> library to query its callbacks to ensure that only 'live' callbacks are
>> called. How would you handle closures?
> 
> Sorry, don't understand the question.

You were saying that you move your objects into a zombie state.  I assumed that you meant you marked them in some manner (e.g., setting 'is_zombie' to True), so that anything that has a strong reference to the object knows the object is not supposed to be used anymore.  That way, regardless of where or how many times you've registered your object for callbacks, the library can do something like the following (banged out in my mail application, may have typos):

"""
_CALLBACKS = []

def execute_callbacks():
    global _CALLBACKS
    _CALLBACKS = [x for x in _CALLBACKS if not x.is_zombie]
    for x in _CALLBACKS:
        x()
"""

That will lazily unregister callbacks that are in the zombie state, which will eventually lead to their collection by the garbage collector.  It won't work for anything that you don't have a reference for (lambdas, etc.), but it should work in a lot of cases.

Is this what you meant?

Thanks,
Cem Karan

[toc] | [prev] | [next] | [standalone]


#86115

FromMarko Rauhamaa <marko@pacujo.net>
Date2015-02-22 15:54 +0200
Message-ID<8761au9hmr.fsf@elektro.pacujo.net>
In reply to#86113
Cem Karan <cfkaran2@gmail.com>:

> You were saying that you move your objects into a zombie state.  I
> assumed that you meant you marked them in some manner (e.g., setting
> 'is_zombie' to True),

Yes, but even better:

    self.set_state(ZOMBIE)

>  so that anything that has a strong reference to the object knows the
>  object is not supposed to be used anymore.

The other way round: the zombie object knows to ignore callbacks sent
its way. It's not the responsibility of the sender to mind the
receiver's internal state.

I nowadays tend to implement states as inner classes. Here's how I've
implemented the zombie state of one class:

class Delivery...:
    def __init__(...):
        ...
        class ZOMBIE(STATE):
            def handle_connected(self):
                pass
            def handle_eof(self):
                pass
            def handle_response(self, code, response):
                pass
            def handle_io_error(self, errcode):
                pass
            def zombifie(self):
                assert False
            def transaction_timeout(self):
                assert False


Marko

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web