1
1
"""Command line options, ini-file and conftest.py processing."""
2
2
import argparse
3
3
import collections .abc
4
- import contextlib
5
4
import copy
6
5
import enum
7
6
import inspect
@@ -345,14 +344,19 @@ def __init__(self) -> None:
345
344
import _pytest .assertion
346
345
347
346
super ().__init__ ("pytest" )
348
- # The objects are module objects, only used generically.
349
- self ._conftest_plugins : Set [types .ModuleType ] = set ()
350
347
351
- # State related to local conftest plugins.
348
+ # -- State related to local conftest plugins.
349
+ # All loaded conftest modules.
350
+ self ._conftest_plugins : Set [types .ModuleType ] = set ()
351
+ # All conftest modules applicable for a directory.
352
+ # This includes the directory's own conftest modules as well
353
+ # as those of its parent directories.
352
354
self ._dirpath2confmods : Dict [Path , List [types .ModuleType ]] = {}
353
- self . _conftestpath2mod : Dict [ Path , types . ModuleType ] = {}
355
+ # Cutoff directory above which conftests are no longer discovered.
354
356
self ._confcutdir : Optional [Path ] = None
357
+ # If set, conftest loading is skipped.
355
358
self ._noconftest = False
359
+
356
360
self ._duplicatepaths : Set [Path ] = set ()
357
361
358
362
# plugins that were explicitly skipped with pytest.skip
@@ -514,6 +518,19 @@ def _set_initial_conftests(
514
518
if not foundanchor :
515
519
self ._try_load_conftest (current , namespace .importmode , rootpath )
516
520
521
+ def _is_in_confcutdir (self , path : Path ) -> bool :
522
+ """Whether a path is within the confcutdir.
523
+
524
+ When false, should not load conftest.
525
+ """
526
+ if self ._confcutdir is None :
527
+ return True
528
+ try :
529
+ path .relative_to (self ._confcutdir )
530
+ except ValueError :
531
+ return False
532
+ return True
533
+
517
534
def _try_load_conftest (
518
535
self , anchor : Path , importmode : Union [str , ImportMode ], rootpath : Path
519
536
) -> None :
@@ -526,7 +543,7 @@ def _try_load_conftest(
526
543
527
544
def _getconftestmodules (
528
545
self , path : Path , importmode : Union [str , ImportMode ], rootpath : Path
529
- ) -> List [types .ModuleType ]:
546
+ ) -> Sequence [types .ModuleType ]:
530
547
if self ._noconftest :
531
548
return []
532
549
@@ -545,14 +562,12 @@ def _getconftestmodules(
545
562
# and allow users to opt into looking into the rootdir parent
546
563
# directories instead of requiring to specify confcutdir.
547
564
clist = []
548
- confcutdir_parents = self ._confcutdir .parents if self ._confcutdir else []
549
565
for parent in reversed ((directory , * directory .parents )):
550
- if parent in confcutdir_parents :
551
- continue
552
- conftestpath = parent / "conftest.py"
553
- if conftestpath .is_file ():
554
- mod = self ._importconftest (conftestpath , importmode , rootpath )
555
- clist .append (mod )
566
+ if self ._is_in_confcutdir (parent ):
567
+ conftestpath = parent / "conftest.py"
568
+ if conftestpath .is_file ():
569
+ mod = self ._importconftest (conftestpath , importmode , rootpath )
570
+ clist .append (mod )
556
571
self ._dirpath2confmods [directory ] = clist
557
572
return clist
558
573
@@ -574,15 +589,9 @@ def _rget_with_confmod(
574
589
def _importconftest (
575
590
self , conftestpath : Path , importmode : Union [str , ImportMode ], rootpath : Path
576
591
) -> types .ModuleType :
577
- # Use a resolved Path object as key to avoid loading the same conftest
578
- # twice with build systems that create build directories containing
579
- # symlinks to actual files.
580
- # Using Path().resolve() is better than py.path.realpath because
581
- # it resolves to the correct path/drive in case-insensitive file systems (#5792)
582
- key = conftestpath .resolve ()
583
-
584
- with contextlib .suppress (KeyError ):
585
- return self ._conftestpath2mod [key ]
592
+ existing = self .get_plugin (str (conftestpath ))
593
+ if existing is not None :
594
+ return cast (types .ModuleType , existing )
586
595
587
596
pkgpath = resolve_package_path (conftestpath )
588
597
if pkgpath is None :
@@ -598,11 +607,10 @@ def _importconftest(
598
607
self ._check_non_top_pytest_plugins (mod , conftestpath )
599
608
600
609
self ._conftest_plugins .add (mod )
601
- self ._conftestpath2mod [key ] = mod
602
610
dirpath = conftestpath .parent
603
611
if dirpath in self ._dirpath2confmods :
604
612
for path , mods in self ._dirpath2confmods .items ():
605
- if path and dirpath in path .parents or path == dirpath :
613
+ if dirpath in path .parents or path == dirpath :
606
614
assert mod not in mods
607
615
mods .append (mod )
608
616
self .trace (f"loading conftestmodule { mod !r} " )
0 commit comments