blob: 33340436de9470279a0bb53e483ea625d429481b (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
Semi-automatic resource management with AutoClose
---------------------------------------------------
I have read in my life code where the release of a resource was done
in the destructor method, relying on Python reference counting garbage
collector. This kind of code is very fragile, since Python does not
make any guarantee at all about *when* the destructor will be called.
Consider for instance this code (coming from a real life case):
<pre>
$ cat deallocating.py
import logging
class C(object):
def __init__(self):
logging.warn('Allocating resource ...')
def __del__(self):
logging.warn('De-allocating resource ...')
print 'THIS IS NEVER REACHED!'
if __name__ == '__main__':
c = C()
$ python deallocating.py
WARNING:root:Allocating resource ...
Exception exceptions.AttributeError: "'NoneType' object has no
attribute 'warn'" in <bound method C.__del__ of <__main__.C object at
0xb7b9436c>> ignored
</pre>
The issue here is that the destructor is called *too late*, when the
cleanup mechanism (see http://www.python.org/doc/essays/cleanup) has
already set logging to None. This recipe
1) gets rid of the destructor, and put the responsibility of deallocating
the resource in a .close method (which is IMO opinion much clearer);
2) used in conjunction with the contextlib.closing class (new in Python 2.5)
it allows to perform explicit resource deallocation with the 'with'
statement, which is the preferred way to do that in Python 2.5;
3) at the end of the program, it automatically closes the resources which
were not closed explicitely before.
The usage is pretty simple. You should just import from the autoclose module
the AutoClose class and you should add it to the list of your base classes.
Then you should override the .close method making sure it calls the base
class .close.
Here is an example:
<pre>
$ cat autoclose_ex.py
import logging
from autoclose import AutoClose
class C(AutoClose):
def __init__(self, id):
self.id = id
def close(self):
logging.warn('closing object %s' % self.id)
super(C, self).close()
class D(C):
pass
c1 = C(1)
c2 = C(2)
d3 = D(3)
$ python autoclose_ex.py
Closing instances of <class 'autoclose.AutoClose'>
Closing instances of <class '__main__.C'>
WARNING:root:closing object 1
WARNING:root:closing object 2
Closing instances of <class '__main__.D'>
WARNING:root:closing object 3
</pre>
Notice that AutoClose keeps in memory a potentially large lists of instances,
therefore your are advised to explicitly close your objects as soon as
possible, to keep the list short. You can remove all the instances of a
given class (and its subclasses) with the (meta)method <Class>.closeall().
The implementation below should answer all your questions about how this
is done exactly. In particular the call to atexit.register(AutoClose.closeall)
is the one that ensures correct finalization (unlikely destructors methods,
functions registered in atexit do not rely on the garbage collector and
are called before the cleanup mechanism).
|