2
Python related collection nodes.
2
Python related collection nodes.
6
7
from py._test.collect import configproperty, warnoldcollect
7
8
from py._test import funcargs
8
9
from py._code.code import TerminalRepr
10
11
class PyobjMixin(object):
15
except AttributeError:
16
self._obj = obj = self._getobj()
18
def fset(self, value):
20
return property(fget, fset, None, "underlying python object")
16
except AttributeError:
17
self._obj = obj = self._getobj()
19
def fset(self, value):
21
return property(fget, fset, None, "underlying python object")
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
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')
71
def funcnamefilter(self, name):
72
return name.startswith('test')
73
def classnamefilter(self, name):
72
def funcnamefilter(self, name):
73
return name.startswith('test')
74
def classnamefilter(self, name):
74
75
return name.startswith('Test')
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)
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)
137
138
class Module(py.test.collect.File, PyCollectorMixin):
138
139
def _getobj(self):
139
140
return self._memoizedcall('_obj', self._importtestmodule)
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
145
mod = self.fspath.pyimport(ensuresyspath=True)
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"
155
"which is not the same as the test file we want to collect:\n"
157
"HINT: use a unique basename for your test file modules"
144
160
#print "imported test module", mod
145
161
self.config.pluginmanager.consider_module(mod)
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
160
176
self.obj.setup_module()
163
if hasattr(self.obj, 'teardown_module'):
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
178
194
return [self.Instance(name="()", parent=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)
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)
196
class Instance(PyCollectorMixin, py.test.collect.Collector):
198
return self.parent.obj()
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)
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)
212
class Instance(PyCollectorMixin, py.test.collect.Collector):
214
return self.parent.obj()
216
return getattr(self.obj, 'Function',
201
217
PyCollectorMixin.Function.__get__(self)) # XXX for python 2.2
202
218
def _keywords(self):
204
220
Function = property(Function)
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__)
210
def newinstance(self):
226
def newinstance(self):
211
227
self.obj = self._getobj()
215
231
""" mixin for the code common to Function and Generator.
219
235
""" perform setup for this test function. """
220
236
if inspect.ismethod(self.obj):
221
name = 'setup_method'
223
name = 'setup_function'
237
name = 'setup_method'
239
name = 'setup_function'
224
240
if isinstance(self.parent, Instance):
225
241
obj = self.parent.newinstance()
226
242
self.obj = self._getobj()
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)
234
250
""" perform teardown for this test function. """
235
251
if inspect.ismethod(self.obj):
236
name = 'teardown_method'
238
name = 'teardown_function'
239
obj = self.parent.obj
252
name = 'teardown_method'
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)
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()
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,
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"))
270
288
shortfailurerepr = "F"
286
304
tw.line("%s:%d" % (self.filename, self.firstlineno+1))
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)
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,))
301
319
name = "[%d]" % i
319
337
call, args = obj[0], obj[1:]
320
return name, call, args
338
return name, call, args
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.
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)
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"
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
345
363
self.funcargs = {}
346
if callobj is not _dummy:
364
if callobj is not _dummy:
348
366
self.function = getattr(self.obj, 'im_func', self.obj)
367
self.keywords.update(py.builtin._getfuncdict(self.obj) or {})
350
369
def _getobj(self):
357
376
def _isyieldedfunction(self):
358
377
return self._args is not None
360
def readkeywords(self):
361
d = super(Function, self).readkeywords()
362
d.update(py.builtin._getfuncdict(self.obj))
365
379
def runtest(self):
366
380
""" execute the underlying test function. """
367
381
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
370
384
super(Function, self).setup()
371
if hasattr(self, 'funcargs'):
385
if hasattr(self, 'funcargs'):
372
386
funcargs.fillfuncargs(self)
374
388
def __eq__(self, other):
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)
383
397
except AttributeError: