Skip to content

Commit

Permalink
Return support for the broken File/Item hybrids
Browse files Browse the repository at this point in the history
* adds deprecation
* ads necessary support code in node construction
  • Loading branch information
RonnyPfannschmidt committed Jun 5, 2021
1 parent b8cb4fa commit b8604de
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/_pytest/main.py
Expand Up @@ -484,7 +484,7 @@ def __init__(self, config: Config) -> None:

@classmethod
def from_config(cls, config: Config) -> "Session":
session: Session = cls._create(config)
session: Session = cls._create(config=config)
return session

def __repr__(self) -> str:
Expand Down
64 changes: 49 additions & 15 deletions src/_pytest/nodes.py
@@ -1,8 +1,10 @@
import os
import warnings
from inspect import signature
from pathlib import Path
from typing import Any
from typing import Callable
from typing import cast
from typing import Iterable
from typing import Iterator
from typing import List
Expand Down Expand Up @@ -126,7 +128,14 @@ def __call__(self, *k, **kw):
fail(msg, pytrace=False)

def _create(self, *k, **kw):
return super().__call__(*k, **kw)
try:
return super().__call__(*k, **kw)
except TypeError:
sig = signature(cast(Type[Node], self).__init__)
sig.replace()
known_kw = {k: v for k, v in kw.items() if k in sig.parameters}

return super().__call__(*k, **known_kw)


class Node(metaclass=NodeMeta):
Expand Down Expand Up @@ -540,23 +549,35 @@ def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[
class FSCollector(Collector):
def __init__(
self,
fspath: Optional[LEGACY_PATH],
path: Optional[Path],
parent=None,
fspath: Optional[LEGACY_PATH] = None,
path_or_parent: Optional[Union[Path, Node]] = None,
path: Optional[Path] = None,
name: Optional[str] = None,
parent: Optional[Node] = None,
config: Optional[Config] = None,
session: Optional["Session"] = None,
nodeid: Optional[str] = None,
) -> None:
if path_or_parent:
if isinstance(path_or_parent, Node):
assert parent is None
parent = cast(FSCollector, path_or_parent)
elif isinstance(path_or_parent, Path):
assert path is None
path = path_or_parent

assert parent is not None
path, fspath = _imply_path(path, fspath=fspath)
name = path.name
if parent is not None and parent.path != path:
try:
rel = path.relative_to(parent.path)
except ValueError:
pass
else:
name = str(rel)
name = name.replace(os.sep, SEP)
if name is None:
name = path.name
if parent is not None and parent.path != path:
try:
rel = path.relative_to(parent.path)
except ValueError:
pass
else:
name = str(rel)
name = name.replace(os.sep, SEP)
self.path = path

session = session or parent.session
Expand All @@ -571,7 +592,12 @@ def __init__(
nodeid = nodeid.replace(os.sep, SEP)

super().__init__(
name, parent, config, session, nodeid=nodeid, fspath=fspath, path=path
name=name,
parent=parent,
config=config,
session=session,
nodeid=nodeid,
path=path,
)

@classmethod
Expand Down Expand Up @@ -631,8 +657,16 @@ def __init__(
config: Optional[Config] = None,
session: Optional["Session"] = None,
nodeid: Optional[str] = None,
**kw,
) -> None:
super().__init__(name, parent, config, session, nodeid=nodeid)
super().__init__(
name=name,
parent=parent,
config=config,
session=session,
nodeid=nodeid,
**kw,
)
self._report_sections: List[Tuple[str, str, str]] = []

#: A list of tuples (name, value) that holds user defined properties
Expand Down
17 changes: 13 additions & 4 deletions testing/test_nodes.py
Expand Up @@ -39,14 +39,23 @@ def test_node_from_parent_disallowed_arguments() -> None:
nodes.Node.from_parent(None, config=None) # type: ignore[arg-type]


def test_subclassing_both_item_and_collector_warns() -> None:
def test_subclassing_both_item_and_collector_warns(request, tmp_path: Path) -> None:
from _pytest.compat import legacy_path

with pytest.warns(
PytestWarning, match="SoWrong is a Item subclass and should not be a collector"
PytestWarning,
match=(
"(?m)SoWrong is an Item subclass and should not be a collector, however its bases File are collectors.\n"
"Please split the Collectors and the Item into separate node types.\nTODO:.*"
),
):

class SoWrong(nodes.Item, nodes.File):
pass
class SoWrong(nodes.File, nodes.Item):
def __init__(self, fspath, parent):
"""Legacy ctor with legacy call # don't wana see"""
super().__init__(fspath, parent)

SoWrong.from_parent(request.session, fspath=legacy_path(tmp_path / "broken.txt"))


@pytest.mark.parametrize(
Expand Down

0 comments on commit b8604de

Please sign in to comment.