diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 4b54e6a4dab08..fdf44566c3988 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -59,8 +59,8 @@ for full details, see :ref:`running-mypy`. pass ``--exclude '/setup\.py$'``. Similarly, you can ignore discovering directories with a given name by e.g. ``--exclude /build/`` or those matching a subpath with ``--exclude /project/vendor/``. To ignore - multiple files / directories / paths, you can combine expressions with - ``|``, e.g ``--exclude '/setup\.py$|/build/'``. + multiple files / directories / paths, you can provide the --exclude + flag more than once, e.g ``--exclude '/setup\.py$' --exclude '/build/'``. Note that this flag only affects recursive directory tree discovery, that is, when mypy is discovering files within a directory tree or submodules of diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 1241c40e5ac80..a163e738fd212 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -201,7 +201,7 @@ section of the command line docs. A regular expression that matches file names, directory names and paths which mypy should ignore while recursively discovering files to check. - Use forward slashes on all platforms. + Use forward slashes on all platforms. May be specified more than once. For more details, see :option:`--exclude `. diff --git a/mypy/main.py b/mypy/main.py index 9ecd345126f48..e478d65a6fcaf 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -864,11 +864,13 @@ def add_invertible_flag(flag: str, group=code_group) code_group.add_argument( "--exclude", + action="append", metavar="PATTERN", - default="", + default=[], help=( "Regular expression to match file names, directory names or paths which mypy should " - "ignore while recursively discovering files to check, e.g. --exclude '/setup\\.py$'" + "ignore while recursively discovering files to check, e.g. --exclude '/setup\\.py$'. " + "May be specified more than once, eg. --exclude a --exclude b" ) ) code_group.add_argument( diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 17a700d0ce9f3..4469533835e3d 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -500,16 +500,21 @@ def find_modules_recursive(self, module: str) -> List[BuildSource]: return sources -def matches_exclude(subpath: str, exclude: str, fscache: FileSystemCache, verbose: bool) -> bool: - if not exclude: +def matches_exclude(subpath: str, + excludes: List[str], + fscache: FileSystemCache, + verbose: bool) -> bool: + if not excludes: return False subpath_str = os.path.relpath(subpath).replace(os.sep, "/") if fscache.isdir(subpath): subpath_str += "/" - if re.search(exclude, subpath_str): - if verbose: - print("TRACE: Excluding {}".format(subpath_str), file=sys.stderr) - return True + for exclude in excludes: + if re.search(exclude, subpath_str): + if verbose: + print("TRACE: Excluding {} (matches pattern {})".format(subpath_str, exclude), + file=sys.stderr) + return True return False diff --git a/mypy/options.py b/mypy/options.py index 3a56add0d0ad7..738e1ac71ffa7 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -100,7 +100,7 @@ def __init__(self) -> None: # top-level __init__.py to your packages. self.explicit_package_bases = False # File names, directory names or subpaths to avoid checking - self.exclude: str = "" + self.exclude: List[str] = [] # disallow_any options self.disallow_any_generics = False diff --git a/mypy/test/test_find_sources.py b/mypy/test/test_find_sources.py index 7824c82208f97..53da9c384bd21 100644 --- a/mypy/test/test_find_sources.py +++ b/mypy/test/test_find_sources.py @@ -298,7 +298,7 @@ def test_find_sources_exclude(self) -> None: } # file name - options.exclude = r"/f\.py$" + options.exclude = [r"/f\.py$"] fscache = FakeFSCache(files) assert find_sources(["/"], options, fscache) == [ ("a2", "/pkg"), @@ -309,7 +309,7 @@ def test_find_sources_exclude(self) -> None: assert find_sources(["/pkg/a2/b/f.py"], options, fscache) == [('a2.b.f', '/pkg')] # directory name - options.exclude = "/a1/" + options.exclude = ["/a1/"] fscache = FakeFSCache(files) assert find_sources(["/"], options, fscache) == [ ("a2", "/pkg"), @@ -323,13 +323,13 @@ def test_find_sources_exclude(self) -> None: with pytest.raises(InvalidSourceList): find_sources(["/pkg/a1/b"], options, fscache) - options.exclude = "/a1/$" + options.exclude = ["/a1/$"] assert find_sources(["/pkg/a1"], options, fscache) == [ ('e', '/pkg/a1/b/c/d'), ('f', '/pkg/a1/b') ] # paths - options.exclude = "/pkg/a1/" + options.exclude = ["/pkg/a1/"] fscache = FakeFSCache(files) assert find_sources(["/"], options, fscache) == [ ("a2", "/pkg"), @@ -339,15 +339,17 @@ def test_find_sources_exclude(self) -> None: with pytest.raises(InvalidSourceList): find_sources(["/pkg/a1"], options, fscache) - options.exclude = "/(a1|a3)/" - fscache = FakeFSCache(files) - assert find_sources(["/"], options, fscache) == [ - ("a2", "/pkg"), - ("a2.b.c.d.e", "/pkg"), - ("a2.b.f", "/pkg"), - ] + # OR two patterns together + for orred in [["/(a1|a3)/"], ["a1", "a3"], ["a3", "a1"]]: + options.exclude = orred + fscache = FakeFSCache(files) + assert find_sources(["/"], options, fscache) == [ + ("a2", "/pkg"), + ("a2.b.c.d.e", "/pkg"), + ("a2.b.f", "/pkg"), + ] - options.exclude = "b/c/" + options.exclude = ["b/c/"] fscache = FakeFSCache(files) assert find_sources(["/"], options, fscache) == [ ("a2", "/pkg"), @@ -356,19 +358,22 @@ def test_find_sources_exclude(self) -> None: ] # nothing should be ignored as a result of this - options.exclude = "|".join(( + big_exclude1 = [ "/pkg/a/", "/2", "/1", "/pk/", "/kg", "/g.py", "/bc", "/xxx/pkg/a2/b/f.py" "xxx/pkg/a2/b/f.py", - )) - fscache = FakeFSCache(files) - assert len(find_sources(["/"], options, fscache)) == len(files) - - files = { - "pkg/a1/b/c/d/e.py", - "pkg/a1/b/f.py", - "pkg/a2/__init__.py", - "pkg/a2/b/c/d/e.py", - "pkg/a2/b/f.py", - } - fscache = FakeFSCache(files) - assert len(find_sources(["."], options, fscache)) == len(files) + ] + big_exclude2 = ["|".join(big_exclude1)] + for big_exclude in [big_exclude1, big_exclude2]: + options.exclude = big_exclude + fscache = FakeFSCache(files) + assert len(find_sources(["/"], options, fscache)) == len(files) + + files = { + "pkg/a1/b/c/d/e.py", + "pkg/a1/b/f.py", + "pkg/a2/__init__.py", + "pkg/a2/b/c/d/e.py", + "pkg/a2/b/f.py", + } + fscache = FakeFSCache(files) + assert len(find_sources(["."], options, fscache)) == len(files)