1
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
2
# See LICENSE for details.
8
from twisted.trial import unittest
9
from twisted.python import rebuild
11
import crash_test_dummy
12
f = crash_test_dummy.foo
16
class Baz(object): pass
17
class Buz(Bar, Baz): pass
19
class HashRaisesRuntimeError:
21
Things that don't hash (raise an Exception) should be ignored by the
24
@ivar hashCalled: C{bool} set to True when __hash__ is called.
27
self.hashCalled = False
30
self.hashCalled = True
31
raise RuntimeError('not a TypeError!')
35
unhashableObject = None # set in test_hashException
39
class RebuildTestCase(unittest.TestCase):
41
Simple testcase for rebuilding, to at least exercise the code.
44
self.libPath = self.mktemp()
45
os.mkdir(self.libPath)
46
self.fakelibPath = os.path.join(self.libPath, 'twisted_rebuild_fakelib')
47
os.mkdir(self.fakelibPath)
48
file(os.path.join(self.fakelibPath, '__init__.py'), 'w').close()
49
sys.path.insert(0, self.libPath)
52
sys.path.remove(self.libPath)
54
def testFileRebuild(self):
55
from twisted.python.util import sibpath
57
shutil.copyfile(sibpath(__file__, "myrebuilder1.py"),
58
os.path.join(self.fakelibPath, "myrebuilder.py"))
59
from twisted_rebuild_fakelib import myrebuilder
66
from twisted.test import test_rebuild
68
class C(myrebuilder.B):
72
i = myrebuilder.Inherit()
73
self.assertEquals(a.a(), 'a')
74
# necessary because the file has not "changed" if a second has not gone
75
# by in unix. This sucks, but it's not often that you'll be doing more
76
# than one reload per second.
78
shutil.copyfile(sibpath(__file__, "myrebuilder2.py"),
79
os.path.join(self.fakelibPath, "myrebuilder.py"))
80
rebuild.rebuild(myrebuilder)
87
self.assertEquals(b2.b(), 'c')
88
self.assertEquals(b.b(), 'c')
89
self.assertEquals(i.a(), 'd')
90
self.assertEquals(a.a(), 'b')
91
# more work to be done on new-style classes
92
# self.assertEquals(c.b(), 'c')
94
def testRebuild(self):
96
Rebuilding an unchanged module.
98
# This test would actually pass if rebuild was a no-op, but it
99
# ensures rebuild doesn't break stuff while being a less
100
# complex test than testFileRebuild.
102
x = crash_test_dummy.X('a')
104
rebuild.rebuild(crash_test_dummy, doLog=False)
105
# Instance rebuilding is triggered by attribute access.
107
self.failUnlessIdentical(x.__class__, crash_test_dummy.X)
109
self.failUnlessIdentical(f, crash_test_dummy.foo)
111
def testComponentInteraction(self):
112
x = crash_test_dummy.XComponent()
113
x.setAdapter(crash_test_dummy.IX, crash_test_dummy.XA)
114
oldComponent = x.getComponent(crash_test_dummy.IX)
115
rebuild.rebuild(crash_test_dummy, 0)
116
newComponent = x.getComponent(crash_test_dummy.IX)
118
newComponent.method()
120
self.assertEquals(newComponent.__class__, crash_test_dummy.XA)
122
# Test that a duplicate registerAdapter is not allowed
123
from twisted.python import components
124
self.failUnlessRaises(ValueError, components.registerAdapter,
125
crash_test_dummy.XA, crash_test_dummy.X,
128
def testUpdateInstance(self):
139
rebuild.updateInstance(b)
140
assert hasattr(b, 'foo'), "Missing method on rebuilt instance"
141
assert hasattr(b, 'x'), "Missing class attribute on rebuilt instance"
143
def testBananaInteraction(self):
144
from twisted.python import rebuild
145
from twisted.spread import banana
146
rebuild.latestClass(banana.Banana)
149
def test_hashException(self):
151
Rebuilding something that has a __hash__ that raises a non-TypeError
152
shouldn't cause rebuild to die.
154
global unhashableObject
155
unhashableObject = HashRaisesRuntimeError()
157
global unhashableObject
158
unhashableObject = None
159
self.addCleanup(_cleanup)
160
rebuild.rebuild(rebuild)
161
self.assertEquals(unhashableObject.hashCalled, True)
165
class NewStyleTestCase(unittest.TestCase):
167
Tests for rebuilding new-style classes of various sorts.
170
self.m = new.module('whipping')
171
sys.modules['whipping'] = self.m
175
del sys.modules['whipping']
179
def test_slots(self):
181
Try to rebuild a new style class with slots defined.
184
"class SlottedClass(object):\n"
185
" __slots__ = ['a']\n")
187
exec classDefinition in self.m.__dict__
188
inst = self.m.SlottedClass()
190
exec classDefinition in self.m.__dict__
191
rebuild.updateInstance(inst)
192
self.assertEqual(inst.a, 7)
193
self.assertIdentical(type(inst), self.m.SlottedClass)
195
if sys.version_info < (2, 6):
196
test_slots.skip = "__class__ assignment for class with slots is only available starting Python 2.6"
199
def test_errorSlots(self):
201
Try to rebuild a new style class with slots defined: this should fail.
204
"class SlottedClass(object):\n"
205
" __slots__ = ['a']\n")
207
exec classDefinition in self.m.__dict__
208
inst = self.m.SlottedClass()
210
exec classDefinition in self.m.__dict__
211
self.assertRaises(rebuild.RebuildError, rebuild.updateInstance, inst)
213
if sys.version_info >= (2, 6):
214
test_errorSlots.skip = "__class__ assignment for class with slots should work starting Python 2.6"
217
def test_typeSubclass(self):
219
Try to rebuild a base type subclass.
222
"class ListSubclass(list):\n"
225
exec classDefinition in self.m.__dict__
226
inst = self.m.ListSubclass()
228
exec classDefinition in self.m.__dict__
229
rebuild.updateInstance(inst)
230
self.assertEqual(inst[0], 2)
231
self.assertIdentical(type(inst), self.m.ListSubclass)
234
def test_instanceSlots(self):
236
Test that when rebuilding an instance with a __slots__ attribute, it
237
fails accurately instead of giving a L{rebuild.RebuildError}.
240
"class NotSlottedClass(object):\n"
243
exec classDefinition in self.m.__dict__
244
inst = self.m.NotSlottedClass()
245
inst.__slots__ = ['a']
247
"class NotSlottedClass:\n"
249
exec classDefinition in self.m.__dict__
250
# Moving from new-style class to old-style should fail.
251
self.assertRaises(TypeError, rebuild.updateInstance, inst)