Skip to content

Commit 6dac774

Browse files
committed
majorly refactor collection process
- get rid of py.test.collect.Directory alltogether. - introduce direct node.nodeid attribute - remove now superflous attributes on collect and test reports
1 parent f181c70 commit 6dac774

24 files changed

+698
-751
lines changed

CHANGELOG

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ Changes between 1.3.4 and 2.0.0dev0
66
- new python invcation: pytest.main(args, plugins) to load
77
some custom plugins early.
88
- try harder to run unittest test suites in a more compatible manner
9-
by deferring setup/teardown semantics to the unittest package.
9+
by deferring setup/teardown semantics to the unittest package.
1010
- introduce a new way to set config options via ini-style files,
1111
by default setup.cfg and tox.ini files are searched. The old
12-
ways (certain environment variables, dynamic conftest.py reading
12+
ways (certain environment variables, dynamic conftest.py reading
1313
is removed).
1414
- add a new "-q" option which decreases verbosity and prints a more
1515
nose/unittest-style "dot" output.
@@ -26,7 +26,8 @@ Changes between 1.3.4 and 2.0.0dev0
2626
output on assertion failures for comparisons and other cases (Floris Bruynooghe)
2727
- nose-plugin: pass through type-signature failures in setup/teardown
2828
functions instead of not calling them (Ed Singleton)
29-
- major refactoring of internal collection handling
29+
- remove py.test.collect.Directory (follows from a major refactoring
30+
and simplification of the collection process)
3031
- majorly reduce py.test core code, shift function/python testing to own plugin
3132
- fix issue88 (finding custom test nodes from command line arg)
3233
- refine 'tmpdir' creation, will now create basenames better associated

pytest/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
66
(c) Holger Krekel and others, 2004-2010
77
"""
8-
__version__ = '2.0.0.dev18'
8+
__version__ = '2.0.0.dev19'
99

1010
__all__ = ['config', 'cmdline']
1111

1212
from pytest import main as cmdline
1313
UsageError = cmdline.UsageError
14-
main = cmdline.main
14+
main = cmdline.main

pytest/hookspec.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def pytest_runtest_protocol(item):
126126
"""
127127
pytest_runtest_protocol.firstresult = True
128128

129-
def pytest_runtest_logstart(nodeid, location, fspath):
129+
def pytest_runtest_logstart(nodeid, location):
130130
""" signal the start of a test run. """
131131

132132
def pytest_runtest_setup(item):

pytest/main.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ def __init__(self, load=False):
6767
self._hints = []
6868
self.trace = TagTracer().get("pluginmanage")
6969
if os.environ.get('PYTEST_DEBUG'):
70-
self.trace.root.setwriter(sys.stderr.write)
70+
err = sys.stderr
71+
if hasattr(os, 'dup'):
72+
err = py.io.dupfile(err)
73+
self.trace.root.setwriter(err.write)
7174
self.hook = HookRelay([hookspec], pm=self)
7275
self.register(self)
7376
if load:
@@ -370,6 +373,7 @@ def __init__(self, hookrelay, name, firstresult):
370373
self.hookrelay = hookrelay
371374
self.name = name
372375
self.firstresult = firstresult
376+
self.trace = self.hookrelay.trace
373377

374378
def __repr__(self):
375379
return "<HookCaller %r>" %(self.name,)
@@ -380,10 +384,15 @@ def __call__(self, **kwargs):
380384
return mc.execute()
381385

382386
def pcall(self, plugins, **kwargs):
383-
self.hookrelay.trace(self.name, kwargs)
387+
self.trace(self.name, kwargs)
388+
self.trace.root.indent += 1
384389
methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
385390
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
386-
return mc.execute()
391+
res = mc.execute()
392+
if res:
393+
self.trace(res)
394+
self.trace.root.indent -= 1
395+
return res
387396

388397
_preinit = [PluginManager(load=True)] # triggers default plugin importing
389398

pytest/plugin/capture.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,11 @@ def deactivate_funcargs(self):
133133

134134
def pytest_make_collect_report(self, __multicall__, collector):
135135
method = self._getmethod(collector.config, collector.fspath)
136-
self.resumecapture(method)
136+
try:
137+
self.resumecapture(method)
138+
except ValueError:
139+
return # recursive collect, XXX refactor capturing
140+
# to allow for more lightweight recursive capturing
137141
try:
138142
rep = __multicall__.execute()
139143
finally:

pytest/plugin/config.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ def __init__(self, pluginmanager=None):
255255
)
256256
#: a pluginmanager instance
257257
self.pluginmanager = pluginmanager or PluginManager(load=True)
258-
self.trace = self.pluginmanager.trace.get("config")
258+
self.trace = self.pluginmanager.trace.root.get("config")
259259
self._conftest = Conftest(onimport=self._onimportconftest)
260260
self.hook = self.pluginmanager.hook
261261

pytest/plugin/doctest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def pytest_addoption(parser):
1717
def pytest_collect_file(path, parent):
1818
config = parent.config
1919
if path.ext == ".py":
20-
if config.getvalue("doctestmodules"):
20+
if config.option.doctestmodules:
2121
return DoctestModule(path, parent)
2222
elif path.check(fnmatch=config.getvalue("doctestglob")):
2323
return DoctestTextfile(path, parent)

pytest/plugin/junitxml.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import py
6+
import os
67
import time
78

89
def pytest_addoption(parser):
@@ -36,7 +37,9 @@ def __init__(self, logfile, prefix):
3637
self._durations = {}
3738

3839
def _opentestcase(self, report):
39-
names = report.nodenames
40+
names = report.nodeid.split("::")
41+
names[0] = names[0].replace(os.sep, '.')
42+
names = tuple(names)
4043
d = {'time': self._durations.pop(names, "0")}
4144
names = [x.replace(".py", "") for x in names if x != "()"]
4245
classnames = names[:-1]

pytest/plugin/pytester.py

+30-17
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def getcalls(self, names):
105105
return l
106106

107107
def contains(self, entries):
108+
__tracebackhide__ = True
108109
from py.builtin import print_
109110
i = 0
110111
entries = list(entries)
@@ -123,8 +124,7 @@ def contains(self, entries):
123124
break
124125
print_("NONAMEMATCH", name, "with", call)
125126
else:
126-
raise AssertionError("could not find %r in %r" %(
127-
name, self.calls[i:]))
127+
py.test.fail("could not find %r check %r" % (name, check))
128128

129129
def popcall(self, name):
130130
for i, call in enumerate(self.calls):
@@ -278,7 +278,16 @@ def mkpydir(self, name):
278278
Collection = Collection
279279
def getnode(self, config, arg):
280280
collection = Collection(config)
281-
return collection.getbyid(collection._normalizearg(arg))[0]
281+
assert '::' not in str(arg)
282+
p = py.path.local(arg)
283+
x = collection.fspath.bestrelpath(p)
284+
return collection.perform_collect([x], genitems=False)[0]
285+
286+
def getpathnode(self, path):
287+
config = self.parseconfig(path)
288+
collection = Collection(config)
289+
x = collection.fspath.bestrelpath(path)
290+
return collection.perform_collect([x], genitems=False)[0]
282291

283292
def genitems(self, colitems):
284293
collection = colitems[0].collection
@@ -291,8 +300,9 @@ def inline_genitems(self, *args):
291300
#config = self.parseconfig(*args)
292301
config = self.parseconfigure(*args)
293302
rec = self.getreportrecorder(config)
294-
items = Collection(config).perform_collect()
295-
return items, rec
303+
collection = Collection(config)
304+
collection.perform_collect()
305+
return collection.items, rec
296306

297307
def runitem(self, source):
298308
# used from runner functional tests
@@ -469,11 +479,12 @@ def runpytest(self, *args):
469479
p = py.path.local.make_numbered_dir(prefix="runpytest-",
470480
keep=None, rootdir=self.tmpdir)
471481
args = ('--basetemp=%s' % p, ) + args
472-
for x in args:
473-
if '--confcutdir' in str(x):
474-
break
475-
else:
476-
args = ('--confcutdir=.',) + args
482+
#for x in args:
483+
# if '--confcutdir' in str(x):
484+
# break
485+
#else:
486+
# pass
487+
# args = ('--confcutdir=.',) + args
477488
plugins = [x for x in self.plugins if isinstance(x, str)]
478489
if plugins:
479490
args = ('-p', plugins[0]) + args
@@ -530,7 +541,7 @@ def matchreport(self, inamepart="", names="pytest_runtest_logreport pytest_colle
530541
""" return a testreport whose dotted import path matches """
531542
l = []
532543
for rep in self.getreports(names=names):
533-
if not inamepart or inamepart in rep.nodenames:
544+
if not inamepart or inamepart in rep.nodeid.split("::"):
534545
l.append(rep)
535546
if not l:
536547
raise ValueError("could not find test report matching %r: no test reports at all!" %
@@ -616,6 +627,8 @@ def fnmatch_lines_random(self, lines2):
616627
raise ValueError("line %r not found in output" % line)
617628

618629
def fnmatch_lines(self, lines2):
630+
def show(arg1, arg2):
631+
py.builtin.print_(arg1, arg2, file=py.std.sys.stderr)
619632
lines2 = self._getlines(lines2)
620633
lines1 = self.lines[:]
621634
nextline = None
@@ -626,17 +639,17 @@ def fnmatch_lines(self, lines2):
626639
while lines1:
627640
nextline = lines1.pop(0)
628641
if line == nextline:
629-
print_("exact match:", repr(line))
642+
show("exact match:", repr(line))
630643
break
631644
elif fnmatch(nextline, line):
632-
print_("fnmatch:", repr(line))
633-
print_(" with:", repr(nextline))
645+
show("fnmatch:", repr(line))
646+
show(" with:", repr(nextline))
634647
break
635648
else:
636649
if not nomatchprinted:
637-
print_("nomatch:", repr(line))
650+
show("nomatch:", repr(line))
638651
nomatchprinted = True
639-
print_(" and:", repr(nextline))
652+
show(" and:", repr(nextline))
640653
extralines.append(nextline)
641654
else:
642-
assert line == nextline
655+
py.test.fail("remains unmatched: %r, see stderr" % (line,))

pytest/plugin/python.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,10 @@ def pytest_pyfunc_call(__multicall__, pyfuncitem):
4848
def pytest_collect_file(path, parent):
4949
ext = path.ext
5050
pb = path.purebasename
51-
if pb.startswith("test_") or pb.endswith("_test") or \
52-
path in parent.collection._argfspaths:
53-
if ext == ".py":
54-
return parent.ihook.pytest_pycollect_makemodule(
55-
path=path, parent=parent)
51+
if ext == ".py" and (pb.startswith("test_") or pb.endswith("_test") or
52+
parent.collection.isinitpath(path)):
53+
return parent.ihook.pytest_pycollect_makemodule(
54+
path=path, parent=parent)
5655

5756
def pytest_pycollect_makemodule(path, parent):
5857
return Module(path, parent)
@@ -713,11 +712,13 @@ def _raiselookupfailed(self, argname):
713712
def showfuncargs(config):
714713
from pytest.plugin.session import Collection
715714
collection = Collection(config)
716-
firstid = collection._normalizearg(config.args[0])
717-
colitem = collection.getbyid(firstid)[0]
715+
collection.perform_collect()
716+
if collection.items:
717+
plugins = getplugins(collection.items[0])
718+
else:
719+
plugins = getplugins(collection)
718720
curdir = py.path.local()
719721
tw = py.io.TerminalWriter()
720-
plugins = getplugins(colitem, withpy=True)
721722
verbose = config.getvalue("verbose")
722723
for plugin in plugins:
723724
available = []

0 commit comments

Comments
 (0)