11import os
22import warnings
3+ from inspect import signature
34from pathlib import Path
45from typing import Any
56from typing import Callable
7+ from typing import cast
68from typing import Iterable
79from typing import Iterator
810from typing import List
3436from _pytest .pathlib import absolutepath
3537from _pytest .pathlib import commonpath
3638from _pytest .store import Store
39+ from _pytest .warning_types import PytestWarning
3740
3841if TYPE_CHECKING :
3942 # Imported here due to circular import.
@@ -125,7 +128,20 @@ def __call__(self, *k, **kw):
125128 fail (msg , pytrace = False )
126129
127130 def _create (self , * k , ** kw ):
128- return super ().__call__ (* k , ** kw )
131+ try :
132+ return super ().__call__ (* k , ** kw )
133+ except TypeError :
134+ sig = signature (getattr (self , "__init__" ))
135+ known_kw = {k : v for k , v in kw .items () if k in sig .parameters }
136+ from .warning_types import PytestDeprecationWarning
137+
138+ warnings .warn (
139+ PytestDeprecationWarning (
140+ f"{ self } is not using a cooperative constructor and only takes { set (known_kw )} "
141+ )
142+ )
143+
144+ return super ().__call__ (* k , ** known_kw )
129145
130146
131147class Node (metaclass = NodeMeta ):
@@ -539,26 +555,39 @@ def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[
539555class FSCollector (Collector ):
540556 def __init__ (
541557 self ,
542- fspath : Optional [LEGACY_PATH ],
543- path : Optional [Path ],
544- parent = None ,
558+ fspath : Optional [LEGACY_PATH ] = None ,
559+ path_or_parent : Optional [Union [Path , Node ]] = None ,
560+ path : Optional [Path ] = None ,
561+ name : Optional [str ] = None ,
562+ parent : Optional [Node ] = None ,
545563 config : Optional [Config ] = None ,
546564 session : Optional ["Session" ] = None ,
547565 nodeid : Optional [str ] = None ,
548566 ) -> None :
567+ if path_or_parent :
568+ if isinstance (path_or_parent , Node ):
569+ assert parent is None
570+ parent = cast (FSCollector , path_or_parent )
571+ elif isinstance (path_or_parent , Path ):
572+ assert path is None
573+ path = path_or_parent
574+
549575 path , fspath = _imply_path (path , fspath = fspath )
550- name = path .name
551- if parent is not None and parent .path != path :
552- try :
553- rel = path .relative_to (parent .path )
554- except ValueError :
555- pass
556- else :
557- name = str (rel )
558- name = name .replace (os .sep , SEP )
576+ if name is None :
577+ name = path .name
578+ if parent is not None and parent .path != path :
579+ try :
580+ rel = path .relative_to (parent .path )
581+ except ValueError :
582+ pass
583+ else :
584+ name = str (rel )
585+ name = name .replace (os .sep , SEP )
559586 self .path = path
560587
561- session = session or parent .session
588+ if session is None :
589+ assert parent is not None
590+ session = parent .session
562591
563592 if nodeid is None :
564593 try :
@@ -570,7 +599,12 @@ def __init__(
570599 nodeid = nodeid .replace (os .sep , SEP )
571600
572601 super ().__init__ (
573- name , parent , config , session , nodeid = nodeid , fspath = fspath , path = path
602+ name = name ,
603+ parent = parent ,
604+ config = config ,
605+ session = session ,
606+ nodeid = nodeid ,
607+ path = path ,
574608 )
575609
576610 @classmethod
@@ -610,15 +644,37 @@ class Item(Node):
610644
611645 nextitem = None
612646
647+ def __init_subclass__ (cls ) -> None :
648+ problems = ", " .join (
649+ base .__name__ for base in cls .__bases__ if issubclass (base , Collector )
650+ )
651+ if problems :
652+ warnings .warn (
653+ f"{ cls .__name__ } is an Item subclass and should not be a collector, "
654+ f"however its bases { problems } are collectors.\n "
655+ "Please split the Collectors and the Item into separate node types.\n "
656+ "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n "
657+ "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/" ,
658+ PytestWarning ,
659+ )
660+
613661 def __init__ (
614662 self ,
615663 name ,
616664 parent = None ,
617665 config : Optional [Config ] = None ,
618666 session : Optional ["Session" ] = None ,
619667 nodeid : Optional [str ] = None ,
668+ ** kw ,
620669 ) -> None :
621- super ().__init__ (name , parent , config , session , nodeid = nodeid )
670+ super ().__init__ (
671+ name = name ,
672+ parent = parent ,
673+ config = config ,
674+ session = session ,
675+ nodeid = nodeid ,
676+ ** kw ,
677+ )
622678 self ._report_sections : List [Tuple [str , str , str ]] = []
623679
624680 #: A list of tuples (name, value) that holds user defined properties
0 commit comments