diff --git a/_pytest/main.py b/_pytest/main.py index 359945175..9b59e03a2 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -300,7 +300,7 @@ class Session(nodes.FSCollector): def __init__(self, config): nodes.FSCollector.__init__( self, config.rootdir, parent=None, - config=config, session=self) + config=config, session=self, nodeid="") self.testsfailed = 0 self.testscollected = 0 self.shouldstop = False @@ -311,9 +311,6 @@ class Session(nodes.FSCollector): self.config.pluginmanager.register(self, name="session") - def _makeid(self): - return "" - @hookimpl(tryfirst=True) def pytest_collectstart(self): if self.shouldfail: diff --git a/_pytest/nodes.py b/_pytest/nodes.py index 9f51fc884..97f4da602 100644 --- a/_pytest/nodes.py +++ b/_pytest/nodes.py @@ -6,6 +6,8 @@ import py import attr import _pytest +import _pytest._code + from _pytest.mark.structures import NodeKeywords SEP = "/" @@ -69,7 +71,7 @@ class Node(object): """ base class for Collector and Item the test collection tree. Collector subclasses have children, Items are terminal nodes.""" - def __init__(self, name, parent=None, config=None, session=None): + def __init__(self, name, parent=None, config=None, session=None, fspath=None, nodeid=None): #: a unique name within the scope of the parent node self.name = name @@ -83,7 +85,7 @@ class Node(object): self.session = session or parent.session #: filesystem path where this node was collected from (can be None) - self.fspath = getattr(parent, 'fspath', None) + self.fspath = fspath or getattr(parent, 'fspath', None) #: keywords/markers collected from all scopes self.keywords = NodeKeywords(self) @@ -94,6 +96,12 @@ class Node(object): # used for storing artificial fixturedefs for direct parametrization self._name2pseudofixturedef = {} + if nodeid is not None: + self._nodeid = nodeid + else: + assert parent is not None + self._nodeid = self.parent.nodeid + "::" + self.name + @property def ihook(self): """ fspath sensitive hook proxy used to call pytest hooks""" @@ -137,14 +145,7 @@ class Node(object): @property def nodeid(self): """ a ::-separated string denoting its collection tree address. """ - try: - return self._nodeid - except AttributeError: - self._nodeid = x = self._makeid() - return x - - def _makeid(self): - return self.parent.nodeid + "::" + self.name + return self._nodeid def __hash__(self): return hash(self.nodeid) @@ -281,8 +282,14 @@ class Collector(Node): excinfo.traceback = ntraceback.filter() +def _check_initialpaths_for_relpath(session, fspath): + for initial_path in session._initialpaths: + if fspath.common(initial_path) == initial_path: + return fspath.relto(initial_path.dirname) + + class FSCollector(Collector): - def __init__(self, fspath, parent=None, config=None, session=None): + def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None): fspath = py.path.local(fspath) # xxx only for test_resultlog.py? name = fspath.basename if parent is not None: @@ -290,22 +297,19 @@ class FSCollector(Collector): if rel: name = rel name = name.replace(os.sep, SEP) - super(FSCollector, self).__init__(name, parent, config, session) self.fspath = fspath - def _check_initialpaths_for_relpath(self): - for initialpath in self.session._initialpaths: - if self.fspath.common(initialpath) == initialpath: - return self.fspath.relto(initialpath.dirname) + session = session or parent.session - def _makeid(self): - relpath = self.fspath.relto(self.config.rootdir) + if nodeid is None: + nodeid = self.fspath.relto(session.config.rootdir) - if not relpath: - relpath = self._check_initialpaths_for_relpath() - if os.sep != SEP: - relpath = relpath.replace(os.sep, SEP) - return relpath + if not nodeid: + nodeid = _check_initialpaths_for_relpath(session, fspath) + if os.sep != SEP: + nodeid = nodeid.replace(os.sep, SEP) + + super(FSCollector, self).__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) class File(FSCollector): @@ -318,8 +322,8 @@ class Item(Node): """ nextitem = None - def __init__(self, name, parent=None, config=None, session=None): - super(Item, self).__init__(name, parent, config, session) + def __init__(self, name, parent=None, config=None, session=None, nodeid=None): + super(Item, self).__init__(name, parent, config, session, nodeid=nodeid) self._report_sections = [] #: user properties is a list of tuples (name, value) that holds user diff --git a/testing/test_resultlog.py b/testing/test_resultlog.py index 45fed7078..b1760721c 100644 --- a/testing/test_resultlog.py +++ b/testing/test_resultlog.py @@ -13,7 +13,7 @@ def test_generic_path(testdir): from _pytest.main import Session config = testdir.parseconfig() session = Session(config) - p1 = Node('a', config=config, session=session) + p1 = Node('a', config=config, session=session, nodeid='a') # assert p1.fspath is None p2 = Node('B', parent=p1) p3 = Node('()', parent=p2)