17
17
from _pytest .config import hookimpl
18
18
19
19
import _pytest
20
+ from _pytest .main import Session
20
21
import pluggy
21
22
from _pytest import fixtures
22
23
from _pytest import nodes
@@ -157,7 +158,7 @@ def pytest_collect_file(path, parent):
157
158
ext = path .ext
158
159
if ext == ".py" :
159
160
if not parent .session .isinitpath (path ):
160
- for pat in parent .config .getini ('python_files' ):
161
+ for pat in parent .config .getini ('python_files' ) + [ '__init__.py' ] :
161
162
if path .fnmatch (pat ):
162
163
break
163
164
else :
@@ -167,9 +168,23 @@ def pytest_collect_file(path, parent):
167
168
168
169
169
170
def pytest_pycollect_makemodule (path , parent ):
171
+ if path .basename == '__init__.py' :
172
+ return Package (path , parent )
170
173
return Module (path , parent )
171
174
172
175
176
+ def pytest_ignore_collect (path , config ):
177
+ # Skip duplicate packages.
178
+ keepduplicates = config .getoption ("keepduplicates" )
179
+ if keepduplicates :
180
+ duplicate_paths = config .pluginmanager ._duplicatepaths
181
+ if path .basename == '__init__.py' :
182
+ if path in duplicate_paths :
183
+ return True
184
+ else :
185
+ duplicate_paths .add (path )
186
+
187
+
173
188
@hookimpl (hookwrapper = True )
174
189
def pytest_pycollect_makeitem (collector , name , obj ):
175
190
outcome = yield
@@ -475,6 +490,36 @@ def setup(self):
475
490
self .addfinalizer (teardown_module )
476
491
477
492
493
+ class Package (Session , Module ):
494
+
495
+ def __init__ (self , fspath , parent = None , config = None , session = None , nodeid = None ):
496
+ session = parent .session
497
+ nodes .FSCollector .__init__ (
498
+ self , fspath , parent = parent ,
499
+ config = config , session = session , nodeid = nodeid )
500
+ self .name = fspath .pyimport ().__name__
501
+ self .trace = session .trace
502
+ self ._norecursepatterns = session ._norecursepatterns
503
+ for path in list (session .config .pluginmanager ._duplicatepaths ):
504
+ if path .dirname == fspath .dirname and path != fspath :
505
+ session .config .pluginmanager ._duplicatepaths .remove (path )
506
+ pass
507
+
508
+ def isinitpath (self , path ):
509
+ return path in self .session ._initialpaths
510
+
511
+ def collect (self ):
512
+ path = self .fspath .dirpath ()
513
+ pkg_prefix = None
514
+ for path in path .visit (fil = lambda x : 1 ,
515
+ rec = self ._recurse , bf = True , sort = True ):
516
+ if pkg_prefix and pkg_prefix in path .parts ():
517
+ continue
518
+ for x in self ._collectfile (path ):
519
+ yield x
520
+ if isinstance (x , Package ):
521
+ pkg_prefix = path .dirpath ()
522
+
478
523
def _get_xunit_setup_teardown (holder , attr_name , param_obj = None ):
479
524
"""
480
525
Return a callable to perform xunit-style setup or teardown if
0 commit comments