1
1
import os
2
2
import warnings
3
+ from inspect import signature
3
4
from pathlib import Path
4
5
from typing import Any
5
6
from typing import Callable
7
+ from typing import cast
6
8
from typing import Iterable
7
9
from typing import Iterator
8
10
from typing import List
34
36
from _pytest .pathlib import absolutepath
35
37
from _pytest .pathlib import commonpath
36
38
from _pytest .store import Store
39
+ from _pytest .warning_types import PytestWarning
37
40
38
41
if TYPE_CHECKING :
39
42
# Imported here due to circular import.
@@ -125,7 +128,20 @@ def __call__(self, *k, **kw):
125
128
fail (msg , pytrace = False )
126
129
127
130
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 )
129
145
130
146
131
147
class Node (metaclass = NodeMeta ):
@@ -539,26 +555,39 @@ def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[
539
555
class FSCollector (Collector ):
540
556
def __init__ (
541
557
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 ,
545
563
config : Optional [Config ] = None ,
546
564
session : Optional ["Session" ] = None ,
547
565
nodeid : Optional [str ] = None ,
548
566
) -> 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
+
549
575
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 )
559
586
self .path = path
560
587
561
- session = session or parent .session
588
+ if session is None :
589
+ assert parent is not None
590
+ session = parent .session
562
591
563
592
if nodeid is None :
564
593
try :
@@ -570,7 +599,12 @@ def __init__(
570
599
nodeid = nodeid .replace (os .sep , SEP )
571
600
572
601
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 ,
574
608
)
575
609
576
610
@classmethod
@@ -610,15 +644,37 @@ class Item(Node):
610
644
611
645
nextitem = None
612
646
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
+
613
661
def __init__ (
614
662
self ,
615
663
name ,
616
664
parent = None ,
617
665
config : Optional [Config ] = None ,
618
666
session : Optional ["Session" ] = None ,
619
667
nodeid : Optional [str ] = None ,
668
+ ** kw ,
620
669
) -> 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
+ )
622
678
self ._report_sections : List [Tuple [str , str , str ]] = []
623
679
624
680
#: A list of tuples (name, value) that holds user defined properties
0 commit comments