Skip to content

Commit e6f4bd7

Browse files
committed
Add pruning heuristics to PackageFinder based on exclude (#3846)
2 parents 2f4a3c0 + ea4a205 commit e6f4bd7

File tree

2 files changed

+23
-12
lines changed

2 files changed

+23
-12
lines changed

changelog.d/3846.misc.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add pruning heuristics to ``PackageFinder`` based on ``exclude``.

setuptools/discovery.py

+22-12
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
from pathlib import Path
4545
from typing import (
4646
TYPE_CHECKING,
47-
Callable,
4847
Dict,
4948
Iterable,
5049
Iterator,
@@ -61,7 +60,6 @@
6160
from distutils.util import convert_path
6261

6362
_Path = Union[str, os.PathLike]
64-
_Filter = Callable[[str], bool]
6563
StrIter = Iterator[str]
6664

6765
chain_iter = itertools.chain.from_iterable
@@ -75,6 +73,22 @@ def _valid_name(path: _Path) -> bool:
7573
return os.path.basename(path).isidentifier()
7674

7775

76+
class _Filter:
77+
"""
78+
Given a list of patterns, create a callable that will be true only if
79+
the input matches at least one of the patterns.
80+
"""
81+
82+
def __init__(self, *patterns: str):
83+
self._patterns = dict.fromkeys(patterns)
84+
85+
def __call__(self, item: str) -> bool:
86+
return any(fnmatchcase(item, pat) for pat in self._patterns)
87+
88+
def __contains__(self, item: str) -> bool:
89+
return item in self._patterns
90+
91+
7892
class _Finder:
7993
"""Base class that exposes functionality for module/package finders"""
8094

@@ -111,23 +125,15 @@ def find(
111125
return list(
112126
cls._find_iter(
113127
convert_path(str(where)),
114-
cls._build_filter(*cls.ALWAYS_EXCLUDE, *exclude),
115-
cls._build_filter(*include),
128+
_Filter(*cls.ALWAYS_EXCLUDE, *exclude),
129+
_Filter(*include),
116130
)
117131
)
118132

119133
@classmethod
120134
def _find_iter(cls, where: _Path, exclude: _Filter, include: _Filter) -> StrIter:
121135
raise NotImplementedError
122136

123-
@staticmethod
124-
def _build_filter(*patterns: str) -> _Filter:
125-
"""
126-
Given a list of patterns, return a callable that will be true only if
127-
the input matches at least one of the patterns.
128-
"""
129-
return lambda name: any(fnmatchcase(name, pat) for pat in patterns)
130-
131137

132138
class PackageFinder(_Finder):
133139
"""
@@ -160,6 +166,10 @@ def _find_iter(cls, where: _Path, exclude: _Filter, include: _Filter) -> StrIter
160166
if include(package) and not exclude(package):
161167
yield package
162168

169+
# Early pruning if there is nothing else to be scanned
170+
if f"{package}*" in exclude or f"{package}.*" in exclude:
171+
continue
172+
163173
# Keep searching subdirectories, as there may be more packages
164174
# down there, even if the parent was excluded.
165175
dirs.append(dir)

0 commit comments

Comments
 (0)