[Python-Dev] Weakref design questions

David Abrahams dave@boost-consulting.com
18 Oct 2002 20:39:04 -0400


Brian Quinlan <brian@sweetapp.com> writes:

> 1. Is there any reason why builtin methods cannot be proxied? 
> 
> 2. It would be handy for my application if a callback could be triggered
>    when an object has no more weak references attached to it. 
> 
> It seems like my application could be a fairly common one:
> 
> # C library pseudocode
> def c_library_func(): # C code
>     while 1:
>         o = create_complex_object()
> 	  user_call_back(o)
>         del o
> 
> # Python bindings pseudocode
> def python_bindings_user_call_back (o): # C code
>     py_o = create_python_wrapper_object(o)
>     proxy = PyWeakref_NewProxy(py_o)
>     python_function(py_o)
>     Py_DECREF(proxy)
>     Py_DECREF(py_o) # This will kill the proxy

What's the point of the proxy? You never use it for anything. I guess
you must've meant:

     python_function(proxy)

above.

> # Evil Python user code
> evil = None
> def python_function(o):
> 	global evil
> 	o.foo()
> 	evil = o
> 
> start(python_function)
> evil.foo() # Nice exception because evil is a dead proxy

Hum. In Boost.Python the default is to copy the C++ object when
passing it to a Python callback, though it's possible for the user to
explicitly say "just build a Python object around a reference to the
C++ object -- Python code beware of lifetime issues". I guess I like
your idea, though, as a safer alternative for non-copyable or for very
expensive-to-copy C++ objects.

> # More evil Python user code
> 
> more_evil = None
> def python_function(o):
> 	global more_evil
> 	o.foo()
> 	more_evil = o.foo
> 
> start(python_function)
> more_evil() # Crash because the underlying data structures that
> 		# the Python wrapper object depends on are dead 

Hum. 

  >>> class userlist(list): pass # just to get weakref ability
  ...
  >>> l = userlist() + [ 1, 2, 3 ]
  >>> type(l)        # Interesting (buggy?) behavior with 2.2.1
  <type 'list'>
  >>> l = userlist() # Try again
  >>> l += [1,2,3]
  >>> type(l)        # Better
  <class '__main__.userlist'>
  >>> p = weakref.proxy(l)
  >>> a = p.append
  >>> p
  [1, 2, 3]
  >>> a
  <built-in method append of userlist object at 0x0093F328>
  >>> del l
  >>> p              # The list is still alive!
  [1, 2, 3]
  >>> a              # Because it (not the proxy) is bound into a
  <built-in method append of userlist object at 0x0093F328>
  >>> a(2)           # a works without crashing
  >>> p
  [1, 2, 3, 2]

How does this differ from what's happening for you?

> Pruning callable objects that the user is done with is the reason
> for my request.

Hmm, it kinda seems like you want to prune some of them before the
user's done with them. Don't users expect objects to stay alive when
they hold references?

-- 
                    David Abrahams
dave@boost-consulting.com * http://www.boost-consulting.com

Building C/C++ Extensions for Python: Dec 9-11, Austin, TX
http://www.enthought.com/training/building_extensions.html