[Python-Dev] Object finalization for local (ie function) scopes
oliver.schoenborn at utoronto.ca
Wed Jun 9 21:25:25 EDT 2004
This is a module I made to test a concept of finalization *not* based on
__del__. I'm curious to find out if the techniques look like they could be
implemented directly in the Python interpreter to lessen the burden on the
user, which is why I post this here.
- Seems like can't rely on __del__ being called (standard says no guarantee)
- Means a coder of a class for which instances need some sort of
finalization (e.g. file objects need close()) must document textually the
need for finalization
- User of class must remember to use try/finally blocks and position them
judiciously since several objects may require "finalization"; this is easy
to forget if there are multiple (normal or exceptional) exit points.
The technique is based on inheritance: a class that needs finalization is
derived from scope.NeedsFinalization. This class adds info to a special
function, called scope.ScopeGuardian, that wraps a try/finally around the
function that you are interested in making "finalization safe" (ie the
function in which you would normally have to worry about manually putting a
try/finally). This seems to work well and has the advantage that:
- Coder of class makes explicit by base class that finalizaiton is
important, no need to remember to document
- User of class knows just by looking at class def, can't overlook a
sentence saying "call this-that when done with object"
- Some error checking can be done to decrease likelyhood of user forgetting
the try/finallies (which are no longer needed) or of wanting finalization
before ref count goes to 0.
The info added to the function, seems to me, could be done at the
interpreter implementation level, e.g. by adding a new field to function
objects (f_name, etc), such as f_scopedobjs, so it would be almost trivial
to use even for beginners. There are a couple of limitations in the current
implementation that are easy to fix (e.g. make it work for recursive
functions, nested scope guarding, and methods), don't know yet about thread
Anyways, from having looked at the archives, doesn't seem like this
technique has been used, and would't require addition of a new keyword (only
a new class, .e.g as opposed to pep310 approach).
Thanks for any feedback,
-------------- next part --------------
This module provides a class, NeedsFinalization, that you derive from
to indicate that instances of it need finalization upon local
(function) scope. This module also provides a ScopeGuarded() function,
that takes as argument a function and returns a scope-guarded
To use this module in a function that instantiates objects that must
be finalized on exit of function, derive the class of those objects
from NeedsFinalization. Don't forget to call
NeedsFinalization.__init__ in the constructors and to define
_finalize(self) for each class. Then, in the module where your
function is defined, do yourFn = ScopeGuarded(yourFn). This rebinds
your function to be scope-guarded. Now when you call yourFn(), a
special function actually gets called that makes sure all instances of
NeedsFinalization in your original yourFn get deleted. Note that some
runtime error checking is done for you: if you forget to call
NeedsFinalization.__init__(self), or you forget to wrap func but
func contains instances of NeedsFinalization, or if you
# do finalization stuff
def yourFunc(name, greeting):
obj1 = MyClass() # needs finalization
obj2 = 'asdf' # doesn't need it
# do stuff with name, greeting, obj1 and obj2
yourFunc = ScopeGuarded(yourFunc)
This module demonstrates that scope exit for local scopes can have
the equivalent of automatic, guaranteed finalizers, using techniques
that could most likely be implemented inside Python interpreter.
This would bypass the need for re-assigned function names and avoid
the two extra levels of indirection needed in the current
implementation. This would require a built-in class
NeedsFinalization but ScopeGuarded would become hidden from user:
instead, every NeedsFinalization can add itself to a special
built-in field of function objects, and the function would go over
that list upon return and do the finally clause.
- Current implementation doesn't work for recursive functions, or
multiple levels of scope guarding, or multiple threads: this could
probably work if a stack of lists is used for func.f_scopedObjs
instead of a plain list. However, this module is merely a
demonstration of the concept for study.
- Probably doesn't work for diamond inheritence diagrams: the
_finalize() method of root of diamond will get called twice. How
should this situation be trapped? Perhaps instead of asking user to
call base class _finalize(), the user would call
self.finalize_bases(), and that function, implemented in
NeedsFinalization, would make sure things only get called once (if
- Could it be made to work for unbound methods? Docs of inspect module
seem to say no.
- There may be other issues to be resolved, other than the two
above (recursion etc).
:Author: Oliver Schoenborn
:Since: Jun 9, 2004
:Copyright: \(c) 2004 Oliver
:License: Same as for Python
import inspect, sys
"""Return func wrapped such that if you call func(...) you
get the _finalize() method of all instances that derive
from NeedsFinalization, instantiate in func, to be called
upon return, normal or via exception. """
return lambda *args, **kwargs: ScopeGuardian(func, *args, **kwargs)
"""Find ScopeGuardian function that called function
that instantiated us."""
self.__finalized = False
stack = inspect.stack()
lframe = stack
while (lframe is not None) and (lframe.f_code.co_name != 'ScopeGuardian'):
lframe = lframe.f_back
if lframe is None:
msg = '%s: Forgot to scope-guard your function???' % repr(self)
raise RuntimeError, msg
# ok, we're ready
fname = lframe.f_code.co_name
guardedFunc = lframe.f_locals['func']
print '%s: instantiated by scope-guarded %s' % (repr(self), guardedFunc)
"""Derived classes MUST define a self._finalize() method,
where they do their finalization for scope exit."""
print '%s: Finalize() being called' % repr(self)
assert sys.getrefcount(self) == 5
self.__finalized = True
"""This just does some error checking, probably want to remove
in production in case derived objects involved in cycles."""
problem = not self.__finalized
print '%s: NeedsFinalization.__init__ not called for %s' % (repr(self), self.__class__)
raise RuntimeError, msg
if not problem:
print '%s: Finalized properly' % repr(self)
# this is being called because exception was thrown
def ScopeGuardian(func, *args, **kwargs):
"""Make func finalize its locals that said neede finalization.
This is done by wrapping func function in a try/finally block.
When the finally clause is executed, all instances of classes
derived from NeedsFinalization, and instantiated in func, will be
in a special attribute func.f_scopedObjs. We go over that list and
call finalizeMaster() for each item in list."""
func.f_scopedObjs = 
print 'Scoped variables created during call to %s: %s\n\n' \
% (func, func.f_scopedObjs)
if func.f_scopedObjs != :
for obj in func.f_scopedObjs:
func.f_scopedObjs = 
raise RuntimeError, 'finalize() not supposed to be called'
"""Override this. If you class inherits from a
class derived from NeedsFinalization, make sure to
ok = TestFree()
danger = TestDanger()
# test when forget to use ScopeGuardian
hello = func1()
assert False, 'Expected exception not thrown!'
except RuntimeError, msg:
print 'Expected exception caught: ', msg
func1 = ScopeGuarded(func1)
dontKnow = objType()
func2 = ScopeGuarded(func2)
#assert False, 'Expected exception not thrown!'
#except RuntimeError, msg:
#print 'Expected exception caught: ', msg
if __name__ == '__main__':
More information about the Python-Dev