Merge pull request #4358 from blueyed/instance

Node: do not add "::()" to nodeid
This commit is contained in:
Daniel Hahler 2018-11-11 00:42:57 +01:00 committed by GitHub
commit b1312147e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 50 additions and 30 deletions

View File

@ -0,0 +1,18 @@
Remove the ``::()`` notation to denote a test class instance in node ids.
Previously, node ids that contain test instances would use ``::()`` to denote the instance like this::
test_foo.py::Test::()::test_bar
The extra ``::()`` was puzzling to most users and has been removed, so that the test id becomes now::
test_foo.py::Test::test_bar
This change could not accompany a deprecation period as is usual when user-facing functionality changes because
it was not really possible to detect when the functionality was being used explicitly.
The extra ``::()`` might have been removed in some places internally already,
which then led to confusion in places where it was expected, e.g. with
``--deselect`` (`#4127 <https://github.com/pytest-dev/pytest/issues/4127>`_).
Test class instances are also not listed with ``--collect-only`` anymore.

View File

@ -27,7 +27,7 @@ def _splitnode(nodeid):
'' ''
'testing/code' 'testing/code'
'testing/code/test_excinfo.py' 'testing/code/test_excinfo.py'
'testing/code/test_excinfo.py::TestFormattedExcinfo::()' 'testing/code/test_excinfo.py::TestFormattedExcinfo'
Return values are lists e.g. Return values are lists e.g.
[] []
@ -39,7 +39,7 @@ def _splitnode(nodeid):
# If there is no root node at all, return an empty list so the caller's logic can remain sane # If there is no root node at all, return an empty list so the caller's logic can remain sane
return [] return []
parts = nodeid.split(SEP) parts = nodeid.split(SEP)
# Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()' # Replace single last element 'test_foo.py::Bar' with multiple elements 'test_foo.py', 'Bar'
parts[-1:] = parts[-1].split("::") parts[-1:] = parts[-1].split("::")
return parts return parts
@ -47,7 +47,7 @@ def _splitnode(nodeid):
def ischildnode(baseid, nodeid): def ischildnode(baseid, nodeid):
"""Return True if the nodeid is a child node of the baseid. """Return True if the nodeid is a child node of the baseid.
E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp' E.g. 'foo/bar::Baz' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp'
""" """
base_parts = _splitnode(baseid) base_parts = _splitnode(baseid)
node_parts = _splitnode(nodeid) node_parts = _splitnode(nodeid)
@ -107,10 +107,12 @@ class Node(object):
self._name2pseudofixturedef = {} self._name2pseudofixturedef = {}
if nodeid is not None: if nodeid is not None:
assert "::()" not in nodeid
self._nodeid = nodeid self._nodeid = nodeid
else: else:
assert parent is not None self._nodeid = self.parent.nodeid
self._nodeid = self.parent.nodeid + "::" + self.name if self.name != "()":
self._nodeid += "::" + self.name
@property @property
def ihook(self): def ihook(self):

View File

@ -60,8 +60,7 @@ def pytest_terminal_summary(terminalreporter):
tr.write_line("") tr.write_line("")
tr.write_line("(0.00 durations hidden. Use -vv to show these durations.)") tr.write_line("(0.00 durations hidden. Use -vv to show these durations.)")
break break
nodeid = rep.nodeid.replace("::()::", "::") tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, rep.nodeid))
tr.write_line("%02.2fs %-8s %s" % (rep.duration, rep.when, nodeid))
def pytest_sessionstart(session): def pytest_sessionstart(session):

View File

@ -605,9 +605,7 @@ class TerminalReporter(object):
self._tw.line("%s: %d" % (name, count)) self._tw.line("%s: %d" % (name, count))
else: else:
for item in items: for item in items:
nodeid = item.nodeid self._tw.line(item.nodeid)
nodeid = nodeid.replace("::()::", "::")
self._tw.line(nodeid)
return return
stack = [] stack = []
indent = "" indent = ""
@ -619,8 +617,8 @@ class TerminalReporter(object):
stack.pop() stack.pop()
for col in needed_collectors[len(stack) :]: for col in needed_collectors[len(stack) :]:
stack.append(col) stack.append(col)
# if col.name == "()": if col.name == "()": # Skip Instances.
# continue continue
indent = (len(stack) - 1) * " " indent = (len(stack) - 1) * " "
self._tw.line("%s%s" % (indent, col)) self._tw.line("%s%s" % (indent, col))
@ -687,7 +685,7 @@ class TerminalReporter(object):
# collect_fspath comes from testid which has a "/"-normalized path # collect_fspath comes from testid which has a "/"-normalized path
if fspath: if fspath:
res = mkrel(nodeid).replace("::()", "") # parens-normalization res = mkrel(nodeid)
if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace( if self.verbosity >= 2 and nodeid.split("::")[0] != fspath.replace(
"\\", nodes.SEP "\\", nodes.SEP
): ):

View File

@ -1408,9 +1408,7 @@ def test_customize_through_attributes(testdir):
""" """
) )
result = testdir.runpytest("--collect-only") result = testdir.runpytest("--collect-only")
result.stdout.fnmatch_lines( result.stdout.fnmatch_lines(["*MyClass*", "*MyFunction*test_hello*"])
["*MyClass*", "*MyInstance*", "*MyFunction*test_hello*"]
)
def test_unorderable_types(testdir): def test_unorderable_types(testdir):

View File

@ -510,13 +510,8 @@ class TestSession(object):
pass pass
""" """
) )
normid = p.basename + "::TestClass::()::test_method" normid = p.basename + "::TestClass::test_method"
for id in [ for id in [p.basename, p.basename + "::TestClass", normid]:
p.basename,
p.basename + "::TestClass",
p.basename + "::TestClass::()",
normid,
]:
items, hookrec = testdir.inline_genitems(id) items, hookrec = testdir.inline_genitems(id)
assert len(items) == 1 assert len(items) == 1
assert items[0].name == "test_method" assert items[0].name == "test_method"
@ -625,7 +620,7 @@ class TestSession(object):
items, hookrec = testdir.inline_genitems(arg) items, hookrec = testdir.inline_genitems(arg)
assert len(items) == 1 assert len(items) == 1
item, = items item, = items
assert item.nodeid.endswith("TestClass::()::test_method") assert item.nodeid.endswith("TestClass::test_method")
# ensure we are reporting the collection of the single test item (#2464) # ensure we are reporting the collection of the single test item (#2464)
assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"]

View File

@ -8,11 +8,11 @@ from _pytest import nodes
("", "", True), ("", "", True),
("", "foo", True), ("", "foo", True),
("", "foo/bar", True), ("", "foo/bar", True),
("", "foo/bar::TestBaz::()", True), ("", "foo/bar::TestBaz", True),
("foo", "food", False), ("foo", "food", False),
("foo/bar::TestBaz::()", "foo/bar", False), ("foo/bar::TestBaz", "foo/bar", False),
("foo/bar::TestBaz::()", "foo/bar::TestBop::()", False), ("foo/bar::TestBaz", "foo/bar::TestBop", False),
("foo/bar", "foo/bar::TestBop::()", True), ("foo/bar", "foo/bar::TestBop", True),
), ),
) )
def test_ischildnode(baseid, nodeid, expected): def test_ischildnode(baseid, nodeid, expected):

View File

@ -274,16 +274,26 @@ def test_deselect(testdir):
testdir.makepyfile( testdir.makepyfile(
test_a=""" test_a="""
import pytest import pytest
def test_a1(): pass def test_a1(): pass
@pytest.mark.parametrize('b', range(3)) @pytest.mark.parametrize('b', range(3))
def test_a2(b): pass def test_a2(b): pass
class TestClass:
def test_c1(self): pass
def test_c2(self): pass
""" """
) )
result = testdir.runpytest( result = testdir.runpytest(
"-v", "--deselect=test_a.py::test_a2[1]", "--deselect=test_a.py::test_a2[2]" "-v",
"--deselect=test_a.py::test_a2[1]",
"--deselect=test_a.py::test_a2[2]",
"--deselect=test_a.py::TestClass::test_c1",
) )
assert result.ret == 0 assert result.ret == 0
result.stdout.fnmatch_lines(["*2 passed, 2 deselected*"]) result.stdout.fnmatch_lines(["*3 passed, 3 deselected*"])
for line in result.stdout.lines: for line in result.stdout.lines:
assert not line.startswith(("test_a.py::test_a2[1]", "test_a.py::test_a2[2]")) assert not line.startswith(("test_a.py::test_a2[1]", "test_a.py::test_a2[2]"))