~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/rebuild.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_rebuild -*-
 
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
 
6
"""
 
7
*Real* reloading support for Python.
 
8
"""
 
9
 
 
10
# System Imports
 
11
import sys
 
12
import types
 
13
import time
 
14
import linecache
 
15
 
 
16
# Sibling Imports
 
17
from twisted.python import log, reflect
 
18
 
 
19
lastRebuild = time.time()
 
20
 
 
21
 
 
22
class Sensitive:
 
23
    """
 
24
    A utility mixin that's sensitive to rebuilds.
 
25
 
 
26
    This is a mixin for classes (usually those which represent collections of
 
27
    callbacks) to make sure that their code is up-to-date before running.
 
28
    """
 
29
 
 
30
    lastRebuild = lastRebuild
 
31
 
 
32
    def needRebuildUpdate(self):
 
33
        yn = (self.lastRebuild < lastRebuild)
 
34
        return yn
 
35
 
 
36
    def rebuildUpToDate(self):
 
37
        self.lastRebuild = time.time()
 
38
 
 
39
    def latestVersionOf(self, anObject):
 
40
        """
 
41
        Get the latest version of an object.
 
42
 
 
43
        This can handle just about anything callable; instances, functions,
 
44
        methods, and classes.
 
45
        """
 
46
        t = type(anObject)
 
47
        if t == types.FunctionType:
 
48
            return latestFunction(anObject)
 
49
        elif t == types.MethodType:
 
50
            if anObject.im_self is None:
 
51
                return getattr(anObject.im_class, anObject.__name__)
 
52
            else:
 
53
                return getattr(anObject.im_self, anObject.__name__)
 
54
        elif t == types.InstanceType:
 
55
            # Kick it, if it's out of date.
 
56
            getattr(anObject, 'nothing', None)
 
57
            return anObject
 
58
        elif t == types.ClassType:
 
59
            return latestClass(anObject)
 
60
        else:
 
61
            log.msg('warning returning anObject!')
 
62
            return anObject
 
63
 
 
64
_modDictIDMap = {}
 
65
 
 
66
def latestFunction(oldFunc):
 
67
    """
 
68
    Get the latest version of a function.
 
69
    """
 
70
    # This may be CPython specific, since I believe jython instantiates a new
 
71
    # module upon reload.
 
72
    dictID = id(oldFunc.func_globals)
 
73
    module = _modDictIDMap.get(dictID)
 
74
    if module is None:
 
75
        return oldFunc
 
76
    return getattr(module, oldFunc.__name__)
 
77
 
 
78
 
 
79
def latestClass(oldClass):
 
80
    """
 
81
    Get the latest version of a class.
 
82
    """
 
83
    module = reflect.namedModule(oldClass.__module__)
 
84
    newClass = getattr(module, oldClass.__name__)
 
85
    newBases = [latestClass(base) for base in newClass.__bases__]
 
86
 
 
87
    try:
 
88
        # This makes old-style stuff work
 
89
        newClass.__bases__ = tuple(newBases)
 
90
        return newClass
 
91
    except TypeError:
 
92
        if newClass.__module__ == "__builtin__":
 
93
            # __builtin__ members can't be reloaded sanely
 
94
            return newClass
 
95
        ctor = getattr(newClass, '__metaclass__', type)
 
96
        return ctor(newClass.__name__, tuple(newBases), dict(newClass.__dict__))
 
97
 
 
98
 
 
99
class RebuildError(Exception):
 
100
    """
 
101
    Exception raised when trying to rebuild a class whereas it's not possible.
 
102
    """
 
103
 
 
104
 
 
105
def updateInstance(self):
 
106
    """
 
107
    Updates an instance to be current.
 
108
    """
 
109
    try:
 
110
        self.__class__ = latestClass(self.__class__)
 
111
    except TypeError:
 
112
        if hasattr(self.__class__, '__slots__'):
 
113
            raise RebuildError("Can't rebuild class with __slots__ on Python < 2.6")
 
114
        else:
 
115
            raise
 
116
 
 
117
 
 
118
def __getattr__(self, name):
 
119
    """
 
120
    A getattr method to cause a class to be refreshed.
 
121
    """
 
122
    if name == '__del__':
 
123
        raise AttributeError("Without this, Python segfaults.")
 
124
    updateInstance(self)
 
125
    log.msg("(rebuilding stale %s instance (%s))" % (reflect.qual(self.__class__), name))
 
126
    result = getattr(self, name)
 
127
    return result
 
128
 
 
129
 
 
130
def rebuild(module, doLog=1):
 
131
    """
 
132
    Reload a module and do as much as possible to replace its references.
 
133
    """
 
134
    global lastRebuild
 
135
    lastRebuild = time.time()
 
136
    if hasattr(module, 'ALLOW_TWISTED_REBUILD'):
 
137
        # Is this module allowed to be rebuilt?
 
138
        if not module.ALLOW_TWISTED_REBUILD:
 
139
            raise RuntimeError("I am not allowed to be rebuilt.")
 
140
    if doLog:
 
141
        log.msg('Rebuilding %s...' % str(module.__name__))
 
142
 
 
143
    ## Safely handle adapter re-registration
 
144
    from twisted.python import components
 
145
    components.ALLOW_DUPLICATES = True
 
146
 
 
147
    d = module.__dict__
 
148
    _modDictIDMap[id(d)] = module
 
149
    newclasses = {}
 
150
    classes = {}
 
151
    functions = {}
 
152
    values = {}
 
153
    if doLog:
 
154
        log.msg('  (scanning %s): ' % str(module.__name__))
 
155
    for k, v in d.items():
 
156
        if type(v) == types.ClassType:
 
157
            # Failure condition -- instances of classes with buggy
 
158
            # __hash__/__cmp__ methods referenced at the module level...
 
159
            if v.__module__ == module.__name__:
 
160
                classes[v] = 1
 
161
                if doLog:
 
162
                    log.logfile.write("c")
 
163
                    log.logfile.flush()
 
164
        elif type(v) == types.FunctionType:
 
165
            if v.func_globals is module.__dict__:
 
166
                functions[v] = 1
 
167
                if doLog:
 
168
                    log.logfile.write("f")
 
169
                    log.logfile.flush()
 
170
        elif isinstance(v, type):
 
171
            if v.__module__ == module.__name__:
 
172
                newclasses[v] = 1
 
173
                if doLog:
 
174
                    log.logfile.write("o")
 
175
                    log.logfile.flush()
 
176
 
 
177
    values.update(classes)
 
178
    values.update(functions)
 
179
    fromOldModule = values.has_key
 
180
    newclasses = newclasses.keys()
 
181
    classes = classes.keys()
 
182
    functions = functions.keys()
 
183
 
 
184
    if doLog:
 
185
        log.msg('')
 
186
        log.msg('  (reload   %s)' % str(module.__name__))
 
187
 
 
188
    # Boom.
 
189
    reload(module)
 
190
    # Make sure that my traceback printing will at least be recent...
 
191
    linecache.clearcache()
 
192
 
 
193
    if doLog:
 
194
        log.msg('  (cleaning %s): ' % str(module.__name__))
 
195
 
 
196
    for clazz in classes:
 
197
        if getattr(module, clazz.__name__) is clazz:
 
198
            log.msg("WARNING: class %s not replaced by reload!" % reflect.qual(clazz))
 
199
        else:
 
200
            if doLog:
 
201
                log.logfile.write("x")
 
202
                log.logfile.flush()
 
203
            clazz.__bases__ = ()
 
204
            clazz.__dict__.clear()
 
205
            clazz.__getattr__ = __getattr__
 
206
            clazz.__module__ = module.__name__
 
207
    if newclasses:
 
208
        import gc
 
209
    for nclass in newclasses:
 
210
        ga = getattr(module, nclass.__name__)
 
211
        if ga is nclass:
 
212
            log.msg("WARNING: new-class %s not replaced by reload!" % reflect.qual(nclass))
 
213
        else:
 
214
            for r in gc.get_referrers(nclass):
 
215
                if getattr(r, '__class__', None) is nclass:
 
216
                    r.__class__ = ga
 
217
    if doLog:
 
218
        log.msg('')
 
219
        log.msg('  (fixing   %s): ' % str(module.__name__))
 
220
    modcount = 0
 
221
    for mk, mod in sys.modules.items():
 
222
        modcount = modcount + 1
 
223
        if mod == module or mod is None:
 
224
            continue
 
225
 
 
226
        if not hasattr(mod, '__file__'):
 
227
            # It's a builtin module; nothing to replace here.
 
228
            continue
 
229
        changed = 0
 
230
 
 
231
        for k, v in mod.__dict__.items():
 
232
            try:
 
233
                hash(v)
 
234
            except Exception:
 
235
                continue
 
236
            if fromOldModule(v):
 
237
                if type(v) == types.ClassType:
 
238
                    if doLog:
 
239
                        log.logfile.write("c")
 
240
                        log.logfile.flush()
 
241
                    nv = latestClass(v)
 
242
                else:
 
243
                    if doLog:
 
244
                        log.logfile.write("f")
 
245
                        log.logfile.flush()
 
246
                    nv = latestFunction(v)
 
247
                changed = 1
 
248
                setattr(mod, k, nv)
 
249
            else:
 
250
                # Replace bases of non-module classes just to be sure.
 
251
                if type(v) == types.ClassType:
 
252
                    for base in v.__bases__:
 
253
                        if fromOldModule(base):
 
254
                            latestClass(v)
 
255
        if doLog and not changed and ((modcount % 10) ==0) :
 
256
            log.logfile.write(".")
 
257
            log.logfile.flush()
 
258
 
 
259
    components.ALLOW_DUPLICATES = False
 
260
    if doLog:
 
261
        log.msg('')
 
262
        log.msg('   Rebuilt %s.' % str(module.__name__))
 
263
    return module
 
264