~crunch.io/ubuntu/precise/codespeak-lib/unstable

« back to all changes in this revision

Viewing changes to py/_test/pycollect.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2010-08-01 16:24:01 UTC
  • mfrom: (1.1.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20100801162401-g37v49d1p148alpm
Tags: 1.3.3-1
* New upstream release.
* Bump Standards-Version to 3.9.1.
* Fix typo in py.test manpage.
* Prefer Breaks: over Conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
"""
2
 
Python related collection nodes.  
3
 
""" 
 
2
Python related collection nodes.
 
3
"""
4
4
import py
5
5
import inspect
 
6
import sys
6
7
from py._test.collect import configproperty, warnoldcollect
7
8
from py._test import funcargs
8
9
from py._code.code import TerminalRepr
9
10
 
10
11
class PyobjMixin(object):
11
 
    def obj(): 
 
12
    def obj():
12
13
        def fget(self):
13
 
            try: 
14
 
                return self._obj   
15
 
            except AttributeError: 
16
 
                self._obj = obj = self._getobj() 
17
 
                return obj 
18
 
        def fset(self, value): 
19
 
            self._obj = value 
20
 
        return property(fget, fset, None, "underlying python object") 
 
14
            try:
 
15
                return self._obj
 
16
            except AttributeError:
 
17
                self._obj = obj = self._getobj()
 
18
                return obj
 
19
        def fset(self, value):
 
20
            self._obj = value
 
21
        return property(fget, fset, None, "underlying python object")
21
22
    obj = obj()
22
23
 
23
24
    def _getobj(self):
31
32
        for node in chain:
32
33
            if isinstance(node, Instance):
33
34
                continue
34
 
            name = node.name 
 
35
            name = node.name
35
36
            if isinstance(node, Module):
36
37
                assert name.endswith(".py")
37
38
                name = name[:-3]
60
61
    def reportinfo(self):
61
62
        fspath, lineno = self._getfslineno()
62
63
        modpath = self.getmodpath()
63
 
        return fspath, lineno, modpath 
 
64
        return fspath, lineno, modpath
64
65
 
65
 
class PyCollectorMixin(PyobjMixin, py.test.collect.Collector): 
 
66
class PyCollectorMixin(PyobjMixin, py.test.collect.Collector):
66
67
    Class = configproperty('Class')
67
68
    Instance = configproperty('Instance')
68
69
    Function = configproperty('Function')
69
70
    Generator = configproperty('Generator')
70
 
    
71
 
    def funcnamefilter(self, name): 
72
 
        return name.startswith('test') 
73
 
    def classnamefilter(self, name): 
 
71
 
 
72
    def funcnamefilter(self, name):
 
73
        return name.startswith('test')
 
74
    def classnamefilter(self, name):
74
75
        return name.startswith('Test')
75
76
 
76
77
    def collect(self):
111
112
        if self.classnamefilter(name) and \
112
113
           inspect.isclass(obj):
113
114
            if hasinit(obj):
114
 
                # XXX WARN 
 
115
                # XXX WARN
115
116
                return False
116
117
            return True
117
118
 
119
120
        module = self.getparent(Module).obj
120
121
        clscol = self.getparent(Class)
121
122
        cls = clscol and clscol.obj or None
122
 
        metafunc = funcargs.Metafunc(funcobj, config=self.config, 
 
123
        metafunc = funcargs.Metafunc(funcobj, config=self.config,
123
124
            cls=cls, module=module)
124
125
        gentesthook = self.config.hook.pytest_generate_tests
125
126
        plugins = funcargs.getplugins(self, withpy=True)
129
130
        l = []
130
131
        for callspec in metafunc._calls:
131
132
            subname = "%s[%s]" %(name, callspec.id)
132
 
            function = self.Function(name=subname, parent=self, 
 
133
            function = self.Function(name=subname, parent=self,
133
134
                callspec=callspec, callobj=funcobj)
134
135
            l.append(function)
135
136
        return l
136
 
        
 
137
 
137
138
class Module(py.test.collect.File, PyCollectorMixin):
138
139
    def _getobj(self):
139
140
        return self._memoizedcall('_obj', self._importtestmodule)
140
141
 
141
142
    def _importtestmodule(self):
142
 
        # we assume we are only called once per module 
143
 
        mod = self.fspath.pyimport()
 
143
        # we assume we are only called once per module
 
144
        try:
 
145
            mod = self.fspath.pyimport(ensuresyspath=True)
 
146
        except SyntaxError:
 
147
            excinfo = py.code.ExceptionInfo()
 
148
            raise self.CollectError(excinfo.getrepr(style="short"))
 
149
        except self.fspath.ImportMismatchError:
 
150
            e = sys.exc_info()[1]
 
151
            raise self.CollectError(
 
152
                "import file mismatch:\n"
 
153
                "imported module %r has this __file__ attribute:\n"
 
154
                "  %s\n"
 
155
                "which is not the same as the test file we want to collect:\n"
 
156
                "  %s\n"
 
157
                "HINT: use a unique basename for your test file modules"
 
158
                 % e.args
 
159
            )
144
160
        #print "imported test module", mod
145
161
        self.config.pluginmanager.consider_module(mod)
146
162
        return mod
147
163
 
148
 
    def setup(self): 
 
164
    def setup(self):
149
165
        if getattr(self.obj, 'disabled', 0):
150
166
            py.log._apiwarn(">1.1.1", "%r uses 'disabled' which is deprecated, "
151
167
                "use pytestmark=..., see pytest_skipping plugin" % (self.obj,))
152
168
            py.test.skip("%r is disabled" %(self.obj,))
153
 
        if hasattr(self.obj, 'setup_module'): 
 
169
        if hasattr(self.obj, 'setup_module'):
154
170
            #XXX: nose compat hack, move to nose plugin
155
171
            # if it takes a positional arg, its probably a py.test style one
156
172
            # so we pass the current module object
159
175
            else:
160
176
                self.obj.setup_module()
161
177
 
162
 
    def teardown(self): 
163
 
        if hasattr(self.obj, 'teardown_module'): 
 
178
    def teardown(self):
 
179
        if hasattr(self.obj, 'teardown_module'):
164
180
            #XXX: nose compat hack, move to nose plugin
165
181
            # if it takes a positional arg, its probably a py.test style one
166
182
            # so we pass the current module object
169
185
            else:
170
186
                self.obj.teardown_module()
171
187
 
172
 
class Class(PyCollectorMixin, py.test.collect.Collector): 
 
188
class Class(PyCollectorMixin, py.test.collect.Collector):
173
189
 
174
190
    def collect(self):
175
191
        l = self._deprecated_collect()
177
193
            return l
178
194
        return [self.Instance(name="()", parent=self)]
179
195
 
180
 
    def setup(self): 
 
196
    def setup(self):
181
197
        if getattr(self.obj, 'disabled', 0):
182
198
            py.log._apiwarn(">1.1.1", "%r uses 'disabled' which is deprecated, "
183
199
                "use pytestmark=..., see pytest_skipping plugin" % (self.obj,))
184
200
            py.test.skip("%r is disabled" %(self.obj,))
185
201
        setup_class = getattr(self.obj, 'setup_class', None)
186
 
        if setup_class is not None: 
187
 
            setup_class = getattr(setup_class, 'im_func', setup_class) 
188
 
            setup_class(self.obj) 
189
 
 
190
 
    def teardown(self): 
191
 
        teardown_class = getattr(self.obj, 'teardown_class', None) 
192
 
        if teardown_class is not None: 
193
 
            teardown_class = getattr(teardown_class, 'im_func', teardown_class) 
194
 
            teardown_class(self.obj) 
195
 
 
196
 
class Instance(PyCollectorMixin, py.test.collect.Collector): 
197
 
    def _getobj(self): 
198
 
        return self.parent.obj()  
199
 
    def Function(self): 
200
 
        return getattr(self.obj, 'Function', 
 
202
        if setup_class is not None:
 
203
            setup_class = getattr(setup_class, 'im_func', setup_class)
 
204
            setup_class(self.obj)
 
205
 
 
206
    def teardown(self):
 
207
        teardown_class = getattr(self.obj, 'teardown_class', None)
 
208
        if teardown_class is not None:
 
209
            teardown_class = getattr(teardown_class, 'im_func', teardown_class)
 
210
            teardown_class(self.obj)
 
211
 
 
212
class Instance(PyCollectorMixin, py.test.collect.Collector):
 
213
    def _getobj(self):
 
214
        return self.parent.obj()
 
215
    def Function(self):
 
216
        return getattr(self.obj, 'Function',
201
217
                       PyCollectorMixin.Function.__get__(self)) # XXX for python 2.2
202
218
    def _keywords(self):
203
219
        return []
204
220
    Function = property(Function)
205
221
 
206
222
    #def __repr__(self):
207
 
    #    return "<%s of '%s'>" %(self.__class__.__name__, 
 
223
    #    return "<%s of '%s'>" %(self.__class__.__name__,
208
224
    #                         self.parent.obj.__name__)
209
225
 
210
 
    def newinstance(self):  
 
226
    def newinstance(self):
211
227
        self.obj = self._getobj()
212
228
        return self.obj
213
229
 
215
231
    """ mixin for the code common to Function and Generator.
216
232
    """
217
233
 
218
 
    def setup(self): 
 
234
    def setup(self):
219
235
        """ perform setup for this test function. """
220
236
        if inspect.ismethod(self.obj):
221
 
            name = 'setup_method' 
222
 
        else: 
223
 
            name = 'setup_function' 
 
237
            name = 'setup_method'
 
238
        else:
 
239
            name = 'setup_function'
224
240
        if isinstance(self.parent, Instance):
225
241
            obj = self.parent.newinstance()
226
242
            self.obj = self._getobj()
227
243
        else:
228
 
            obj = self.parent.obj 
 
244
            obj = self.parent.obj
229
245
        setup_func_or_method = getattr(obj, name, None)
230
 
        if setup_func_or_method is not None: 
231
 
            setup_func_or_method(self.obj) 
 
246
        if setup_func_or_method is not None:
 
247
            setup_func_or_method(self.obj)
232
248
 
233
 
    def teardown(self): 
 
249
    def teardown(self):
234
250
        """ perform teardown for this test function. """
235
251
        if inspect.ismethod(self.obj):
236
 
            name = 'teardown_method' 
237
 
        else: 
238
 
            name = 'teardown_function' 
239
 
        obj = self.parent.obj 
 
252
            name = 'teardown_method'
 
253
        else:
 
254
            name = 'teardown_function'
 
255
        obj = self.parent.obj
240
256
        teardown_func_or_meth = getattr(obj, name, None)
241
 
        if teardown_func_or_meth is not None: 
242
 
            teardown_func_or_meth(self.obj) 
 
257
        if teardown_func_or_meth is not None:
 
258
            teardown_func_or_meth(self.obj)
243
259
 
244
260
    def _prunetraceback(self, traceback):
245
 
        if hasattr(self, '_obj') and not self.config.option.fulltrace: 
246
 
            code = py.code.Code(self.obj) 
247
 
            path, firstlineno = code.path, code.firstlineno 
 
261
        if hasattr(self, '_obj') and not self.config.option.fulltrace:
 
262
            code = py.code.Code(self.obj)
 
263
            path, firstlineno = code.path, code.firstlineno
248
264
            ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
249
265
            if ntraceback == traceback:
250
266
                ntraceback = ntraceback.cut(path=path)
251
267
                if ntraceback == traceback:
252
268
                    ntraceback = ntraceback.cut(excludepath=py._pydir)
253
269
            traceback = ntraceback.filter()
254
 
        return traceback 
 
270
        return traceback
255
271
 
256
 
    def _repr_failure_py(self, excinfo):
 
272
    def _repr_failure_py(self, excinfo, style="long"):
257
273
        if excinfo.errisinstance(funcargs.FuncargRequest.LookupError):
258
274
            fspath, lineno, msg = self.reportinfo()
259
275
            lines, _ = inspect.getsourcelines(self.obj)
261
277
                if line.strip().startswith('def'):
262
278
                    return FuncargLookupErrorRepr(fspath, lineno,
263
279
            lines[:i+1], str(excinfo.value))
264
 
        return super(FunctionMixin, self)._repr_failure_py(excinfo)
 
280
        return super(FunctionMixin, self)._repr_failure_py(excinfo,
 
281
            style=style)
265
282
 
266
283
    def repr_failure(self, excinfo, outerr=None):
267
284
        assert outerr is None, "XXX outerr usage is deprecated"
268
 
        return self._repr_failure_py(excinfo)
 
285
        return self._repr_failure_py(excinfo,
 
286
            style=self.config.getvalue("tbstyle"))
269
287
 
270
288
    shortfailurerepr = "F"
271
289
 
285
303
        tw.line()
286
304
        tw.line("%s:%d" % (self.filename, self.firstlineno+1))
287
305
 
288
 
class Generator(FunctionMixin, PyCollectorMixin, py.test.collect.Collector): 
 
306
class Generator(FunctionMixin, PyCollectorMixin, py.test.collect.Collector):
289
307
    def collect(self):
290
 
        # test generators are seen as collectors but they also 
291
 
        # invoke setup/teardown on popular request 
 
308
        # test generators are seen as collectors but they also
 
309
        # invoke setup/teardown on popular request
292
310
        # (induced by the common "test_*" naming shared with normal tests)
293
 
        self.config._setupstate.prepare(self) 
 
311
        self.config._setupstate.prepare(self)
294
312
        l = []
295
313
        seen = {}
296
 
        for i, x in enumerate(self.obj()): 
 
314
        for i, x in enumerate(self.obj()):
297
315
            name, call, args = self.getcallargs(x)
298
 
            if not py.builtin.callable(call): 
 
316
            if not py.builtin.callable(call):
299
317
                raise TypeError("%r yielded non callable test %r" %(self.obj, call,))
300
318
            if name is None:
301
319
                name = "[%d]" % i
306
324
            seen[name] = True
307
325
            l.append(self.Function(name, self, args=args, callobj=call))
308
326
        return l
309
 
        
 
327
 
310
328
    def getcallargs(self, obj):
311
329
        if not isinstance(obj, (tuple, list)):
312
330
            obj = (obj,)
317
335
        else:
318
336
            name = None
319
337
        call, args = obj[0], obj[1:]
320
 
        return name, call, args 
321
 
    
 
338
        return name, call, args
 
339
 
322
340
 
323
341
#
324
 
#  Test Items 
 
342
#  Test Items
325
343
#
326
344
_dummy = object()
327
 
class Function(FunctionMixin, py.test.collect.Item): 
328
 
    """ a Function Item is responsible for setting up  
 
345
class Function(FunctionMixin, py.test.collect.Item):
 
346
    """ a Function Item is responsible for setting up
329
347
        and executing a Python callable test object.
330
348
    """
331
349
    _genid = None
332
350
    def __init__(self, name, parent=None, args=None, config=None,
333
351
                 callspec=None, callobj=_dummy):
334
352
        super(Function, self).__init__(name, parent, config=config)
335
 
        self._args = args 
 
353
        self._args = args
336
354
        if self._isyieldedfunction():
337
 
            assert not callspec, "yielded functions (deprecated) cannot have funcargs" 
 
355
            assert not callspec, "yielded functions (deprecated) cannot have funcargs"
338
356
        else:
339
357
            if callspec is not None:
340
358
                self.funcargs = callspec.funcargs or {}
341
 
                self._genid = callspec.id 
 
359
                self._genid = callspec.id
342
360
                if hasattr(callspec, "param"):
343
361
                    self._requestparam = callspec.param
344
362
            else:
345
363
                self.funcargs = {}
346
 
        if callobj is not _dummy: 
347
 
            self._obj = callobj 
 
364
        if callobj is not _dummy:
 
365
            self._obj = callobj
348
366
        self.function = getattr(self.obj, 'im_func', self.obj)
 
367
        self.keywords.update(py.builtin._getfuncdict(self.obj) or {})
349
368
 
350
369
    def _getobj(self):
351
370
        name = self.name
357
376
    def _isyieldedfunction(self):
358
377
        return self._args is not None
359
378
 
360
 
    def readkeywords(self):
361
 
        d = super(Function, self).readkeywords()
362
 
        d.update(py.builtin._getfuncdict(self.obj))
363
 
        return d
364
 
 
365
379
    def runtest(self):
366
380
        """ execute the underlying test function. """
367
381
        self.ihook.pytest_pyfunc_call(pyfuncitem=self)
368
382
 
369
383
    def setup(self):
370
384
        super(Function, self).setup()
371
 
        if hasattr(self, 'funcargs'): 
 
385
        if hasattr(self, 'funcargs'):
372
386
            funcargs.fillfuncargs(self)
373
387
 
374
388
    def __eq__(self, other):
375
389
        try:
376
 
            return (self.name == other.name and 
 
390
            return (self.name == other.name and
377
391
                    self._args == other._args and
378
392
                    self.parent == other.parent and
379
 
                    self.obj == other.obj and 
380
 
                    getattr(self, '_genid', None) == 
381
 
                    getattr(other, '_genid', None) 
 
393
                    self.obj == other.obj and
 
394
                    getattr(self, '_genid', None) ==
 
395
                    getattr(other, '_genid', None)
382
396
            )
383
397
        except AttributeError:
384
398
            pass
386
400
 
387
401
    def __ne__(self, other):
388
402
        return not self == other
389
 
    
 
403
 
390
404
    def __hash__(self):
391
405
        return hash((self.parent, self.name))
392
406
 
393
407
def hasinit(obj):
394
408
    init = getattr(obj, '__init__', None)
395
409
    if init:
396
 
        if not isinstance(init, type(object.__init__)):
 
410
        if init != object.__init__:
397
411
            return True