37
37
from _pytest .outcomes import exit
38
38
from _pytest .pathlib import absolutepath
39
39
from _pytest .pathlib import bestrelpath
40
+ from _pytest .pathlib import fnmatch_ex
40
41
from _pytest .pathlib import visit
41
42
from _pytest .reports import CollectReport
42
43
from _pytest .reports import TestReport
@@ -353,11 +354,14 @@ def pytest_runtestloop(session: "Session") -> bool:
353
354
return True
354
355
355
356
356
- def _in_venv (path : py . path . local ) -> bool :
357
+ def _in_venv (path : Path ) -> bool :
357
358
"""Attempt to detect if ``path`` is the root of a Virtual Environment by
358
359
checking for the existence of the appropriate activate script."""
359
- bindir = path .join ("Scripts" if sys .platform .startswith ("win" ) else "bin" )
360
- if not bindir .isdir ():
360
+ bindir = path .joinpath ("Scripts" if sys .platform .startswith ("win" ) else "bin" )
361
+ try :
362
+ if not bindir .is_dir ():
363
+ return False
364
+ except OSError :
361
365
return False
362
366
activates = (
363
367
"activate" ,
@@ -367,33 +371,32 @@ def _in_venv(path: py.path.local) -> bool:
367
371
"Activate.bat" ,
368
372
"Activate.ps1" ,
369
373
)
370
- return any ([ fname .basename in activates for fname in bindir .listdir ()] )
374
+ return any (fname .name in activates for fname in bindir .iterdir () )
371
375
372
376
373
- def pytest_ignore_collect (path : py .path .local , config : Config ) -> Optional [bool ]:
374
- path_ = Path (path )
375
- ignore_paths = config ._getconftest_pathlist ("collect_ignore" , path = path_ .parent )
377
+ def pytest_ignore_collect (fspath : Path , config : Config ) -> Optional [bool ]:
378
+ ignore_paths = config ._getconftest_pathlist ("collect_ignore" , path = fspath .parent )
376
379
ignore_paths = ignore_paths or []
377
380
excludeopt = config .getoption ("ignore" )
378
381
if excludeopt :
379
382
ignore_paths .extend (absolutepath (x ) for x in excludeopt )
380
383
381
- if path_ in ignore_paths :
384
+ if fspath in ignore_paths :
382
385
return True
383
386
384
387
ignore_globs = config ._getconftest_pathlist (
385
- "collect_ignore_glob" , path = path_ .parent
388
+ "collect_ignore_glob" , path = fspath .parent
386
389
)
387
390
ignore_globs = ignore_globs or []
388
391
excludeglobopt = config .getoption ("ignore_glob" )
389
392
if excludeglobopt :
390
393
ignore_globs .extend (absolutepath (x ) for x in excludeglobopt )
391
394
392
- if any (fnmatch .fnmatch (str (path ), str (glob )) for glob in ignore_globs ):
395
+ if any (fnmatch .fnmatch (str (fspath ), str (glob )) for glob in ignore_globs ):
393
396
return True
394
397
395
398
allow_in_venv = config .getoption ("collect_in_virtualenv" )
396
- if not allow_in_venv and _in_venv (path ):
399
+ if not allow_in_venv and _in_venv (fspath ):
397
400
return True
398
401
return None
399
402
@@ -538,21 +541,21 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool:
538
541
if ihook .pytest_ignore_collect (fspath = fspath , path = path , config = self .config ):
539
542
return False
540
543
norecursepatterns = self .config .getini ("norecursedirs" )
541
- if any (path . check ( fnmatch = pat ) for pat in norecursepatterns ):
544
+ if any (fnmatch_ex ( pat , fspath ) for pat in norecursepatterns ):
542
545
return False
543
546
return True
544
547
545
548
def _collectfile (
546
- self , path : py . path . local , handle_dupes : bool = True
549
+ self , fspath : Path , handle_dupes : bool = True
547
550
) -> Sequence [nodes .Collector ]:
548
- fspath = Path ( path )
551
+ path = py . path . local ( fspath )
549
552
assert (
550
- path . isfile ()
553
+ fspath . is_file ()
551
554
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})" .format (
552
- path , path . isdir (), path .exists (), path . islink ()
555
+ fspath , fspath . is_dir (), fspath .exists (), fspath . is_symlink ()
553
556
)
554
- ihook = self .gethookproxy (path )
555
- if not self .isinitpath (path ):
557
+ ihook = self .gethookproxy (fspath )
558
+ if not self .isinitpath (fspath ):
556
559
if ihook .pytest_ignore_collect (
557
560
fspath = fspath , path = path , config = self .config
558
561
):
@@ -562,10 +565,10 @@ def _collectfile(
562
565
keepduplicates = self .config .getoption ("keepduplicates" )
563
566
if not keepduplicates :
564
567
duplicate_paths = self .config .pluginmanager ._duplicatepaths
565
- if path in duplicate_paths :
568
+ if fspath in duplicate_paths :
566
569
return ()
567
570
else :
568
- duplicate_paths .add (path )
571
+ duplicate_paths .add (fspath )
569
572
570
573
return ihook .pytest_collect_file (fspath = fspath , path = path , parent = self ) # type: ignore[no-any-return]
571
574
@@ -652,10 +655,8 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]:
652
655
from _pytest .python import Package
653
656
654
657
# Keep track of any collected nodes in here, so we don't duplicate fixtures.
655
- node_cache1 : Dict [py .path .local , Sequence [nodes .Collector ]] = {}
656
- node_cache2 : Dict [
657
- Tuple [Type [nodes .Collector ], py .path .local ], nodes .Collector
658
- ] = ({})
658
+ node_cache1 : Dict [Path , Sequence [nodes .Collector ]] = {}
659
+ node_cache2 : Dict [Tuple [Type [nodes .Collector ], Path ], nodes .Collector ] = ({})
659
660
660
661
# Keep track of any collected collectors in matchnodes paths, so they
661
662
# are not collected more than once.
@@ -679,31 +680,31 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]:
679
680
break
680
681
681
682
if parent .is_dir ():
682
- pkginit = py . path . local ( parent / "__init__.py" )
683
- if pkginit .isfile () and pkginit not in node_cache1 :
683
+ pkginit = parent / "__init__.py"
684
+ if pkginit .is_file () and pkginit not in node_cache1 :
684
685
col = self ._collectfile (pkginit , handle_dupes = False )
685
686
if col :
686
687
if isinstance (col [0 ], Package ):
687
688
pkg_roots [str (parent )] = col [0 ]
688
- node_cache1 [col [0 ].fspath ] = [col [0 ]]
689
+ node_cache1 [Path ( col [0 ].fspath ) ] = [col [0 ]]
689
690
690
691
# If it's a directory argument, recurse and look for any Subpackages.
691
692
# Let the Package collector deal with subnodes, don't collect here.
692
693
if argpath .is_dir ():
693
694
assert not names , "invalid arg {!r}" .format ((argpath , names ))
694
695
695
- seen_dirs : Set [py . path . local ] = set ()
696
+ seen_dirs : Set [Path ] = set ()
696
697
for direntry in visit (str (argpath ), self ._recurse ):
697
698
if not direntry .is_file ():
698
699
continue
699
700
700
- path = py . path . local (direntry .path )
701
- dirpath = path .dirpath ()
701
+ path = Path (direntry .path )
702
+ dirpath = path .parent
702
703
703
704
if dirpath not in seen_dirs :
704
705
# Collect packages first.
705
706
seen_dirs .add (dirpath )
706
- pkginit = dirpath . join ( "__init__.py" )
707
+ pkginit = dirpath / "__init__.py"
707
708
if pkginit .exists ():
708
709
for x in self ._collectfile (pkginit ):
709
710
yield x
@@ -714,23 +715,22 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]:
714
715
continue
715
716
716
717
for x in self ._collectfile (path ):
717
- key = (type (x ), x .fspath )
718
- if key in node_cache2 :
719
- yield node_cache2 [key ]
718
+ key2 = (type (x ), Path ( x .fspath ) )
719
+ if key2 in node_cache2 :
720
+ yield node_cache2 [key2 ]
720
721
else :
721
- node_cache2 [key ] = x
722
+ node_cache2 [key2 ] = x
722
723
yield x
723
724
else :
724
725
assert argpath .is_file ()
725
726
726
- argpath_ = py .path .local (argpath )
727
- if argpath_ in node_cache1 :
728
- col = node_cache1 [argpath_ ]
727
+ if argpath in node_cache1 :
728
+ col = node_cache1 [argpath ]
729
729
else :
730
- collect_root = pkg_roots .get (argpath_ . dirname , self )
731
- col = collect_root ._collectfile (argpath_ , handle_dupes = False )
730
+ collect_root = pkg_roots .get (str ( argpath . parent ) , self )
731
+ col = collect_root ._collectfile (argpath , handle_dupes = False )
732
732
if col :
733
- node_cache1 [argpath_ ] = col
733
+ node_cache1 [argpath ] = col
734
734
735
735
matching = []
736
736
work : List [
@@ -846,7 +846,7 @@ def resolve_collection_argument(
846
846
847
847
This function ensures the path exists, and returns a tuple:
848
848
849
- (py.path.path ("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"])
849
+ (Path ("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"])
850
850
851
851
When as_pypath is True, expects that the command-line argument actually contains
852
852
module paths instead of file-system paths:
0 commit comments