~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lazy_import.py

  • Committer: Benji York
  • Date: 2011-08-19 18:02:37 UTC
  • mto: This revision was merged to the branch mainline in revision 6101.
  • Revision ID: benji@benjiyork.com-20110819180237-2n0sdcfs5v7bxksi
fix bug 702914

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Test the lazy_import functionality."""
18
18
 
 
19
import linecache
19
20
import os
 
21
import re
20
22
import sys
21
23
 
22
24
from bzrlib import (
1167
1169
                          ('_import', 'root8'),
1168
1170
                          ('import', self.root_name, []),
1169
1171
                         ], self.actions)
 
1172
 
 
1173
class TestScopeReplacerReentrance(TestCase):
 
1174
    """The ScopeReplacer should be reentrant.
 
1175
 
 
1176
    Invoking a replacer while an invocation was already on-going leads to a
 
1177
    race to see which invocation will be the first to delete the _factory and
 
1178
    _scope attributes.  The loosing caller used to see AttributeErrors (bug
 
1179
    702914).
 
1180
 
 
1181
    These tests set up a tracer that stops at the moment just before one of
 
1182
    the attributes is being deleted and starts another call to the
 
1183
    functionality in question (__call__, __getattribute__, __setattr_) in
 
1184
    order win the race, setting up the originall caller to loose.
 
1185
    """
 
1186
 
 
1187
    def tracer(self, frame, event, arg):
 
1188
        # Grab the name of the file that contains the code being executed.
 
1189
        filename = frame.f_globals["__file__"]
 
1190
        # Convert ".pyc" and ".pyo" file names to their ".py" equivalent.
 
1191
        filename = re.sub(r'\.py[co]$', '.py', filename)
 
1192
        # If we're executing a line of code from the right module...
 
1193
        if event == 'line' and 'lazy_import.py' in filename:
 
1194
            line = linecache.getline(filename, frame.f_lineno)
 
1195
            # ...and the line of code is the one we're looking for...
 
1196
            if 'del self._factory' in line:
 
1197
                # We don't need to trace any more.
 
1198
                sys.settrace(None)
 
1199
                # Run another racer.  This one will "win" the race, deleting
 
1200
                # the attributes.  When the first racer resumes it will loose
 
1201
                # the race, generating an AttributeError.
 
1202
                self.racer()
 
1203
        return self.tracer
 
1204
 
 
1205
    def run_race(self, racer):
 
1206
        self.racer = racer
 
1207
        sys.settrace(self.tracer)
 
1208
        self.racer() # Should not raise an AttributeError
 
1209
        # Make sure the tracer actually found the code it was looking for.  If
 
1210
        # not, maybe the code was refactored in such a way that these tests
 
1211
        # aren't needed any more.
 
1212
        self.assertEqual(None, sys.gettrace())
 
1213
 
 
1214
    def test_call(self):
 
1215
        def factory(*args):
 
1216
            return factory
 
1217
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
 
1218
        self.run_race(replacer)
 
1219
 
 
1220
    def test_setattr(self):
 
1221
        class Replaced:
 
1222
            pass
 
1223
 
 
1224
        def factory(*args):
 
1225
            return Replaced()
 
1226
 
 
1227
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
 
1228
 
 
1229
        def racer():
 
1230
            replacer.foo = 42
 
1231
 
 
1232
        self.run_race(racer)
 
1233
 
 
1234
    def test_getattribute(self):
 
1235
        class Replaced:
 
1236
            foo = 'bar'
 
1237
 
 
1238
        def factory(*args):
 
1239
            return Replaced()
 
1240
 
 
1241
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
 
1242
 
 
1243
        def racer():
 
1244
            replacer.foo
 
1245
 
 
1246
        self.run_race(racer)