diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index c34f23d9e169..c5d819a4d66c 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -214,6 +214,35 @@ section of the command line docs. This option may only be set in the global section (``[mypy]``). + .. note:: + + Note that the TOML equivalent differs slightly. It can be either a single string + (including a multi-line string) -- which is treated as a single regular + expression -- or an array of such strings. The following TOML examples are + equivalent to the above INI example. + + Array of strings: + + .. code-block:: toml + + [tool.mypy] + exclude = [ + "^file1\\.py$", # TOML's double-quoted strings require escaping backslashes + '^file2\.py$', # but TOML's single-quoted strings do not + ] + + A single, multi-line string: + + .. code-block:: toml + + [tool.mypy] + exclude = '''(?x)( + ^file1\.py$ + |^file2\.py$, + )''' + + See :ref:`using-a-pyproject-toml`. + .. confval:: namespace_packages :type: boolean @@ -907,6 +936,8 @@ These options may only be set in the global section (``[mypy]``). Controls how much debug output will be generated. Higher numbers are more verbose. +.. _using-a-pyproject-toml: + Using a pyproject.toml file *************************** @@ -965,6 +996,10 @@ of your repo (or append it to the end of an existing ``pyproject.toml`` file) an python_version = "2.7" warn_return_any = true warn_unused_configs = true + exclude = [ + '^file1\.py$', # TOML literal string (single-quotes, no escaping necessary) + "^file2\\.py$", # TOML basic string (double-quotes, backslash and other characters need escaping) + ] # mypy per-module options: diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 24e61df0441c..4185179605c9 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -57,6 +57,12 @@ def expand_path(path: str) -> str: return os.path.expandvars(os.path.expanduser(path)) +def str_or_array_as_list(v: Union[str, Sequence[str]]) -> List[str]: + if isinstance(v, str): + return [v.strip()] if v.strip() else [] + return [p.strip() for p in v if p.strip()] + + def split_and_match_files_list(paths: Sequence[str]) -> List[str]: """Take a list of files/directories (with support for globbing through the glob library). @@ -143,6 +149,7 @@ def check_follow_imports(choice: str) -> str: 'disable_error_code': try_split, 'enable_error_code': try_split, 'package_root': try_split, + 'exclude': str_or_array_as_list, }) diff --git a/test-data/unit/cmdline.pyproject.test b/test-data/unit/cmdline.pyproject.test index 83f4745d0786..ea561ed164e7 100644 --- a/test-data/unit/cmdline.pyproject.test +++ b/test-data/unit/cmdline.pyproject.test @@ -80,3 +80,48 @@ def g(a: int) -> int: [out] pyproject.toml: toml config file contains [[tool.mypy.overrides]] sections with conflicting values. Module 'x' has two different values for 'disallow_untyped_defs' == Return code: 0 + +[case testMultilineLiteralExcludePyprojectTOML] +# cmd: mypy x +[file pyproject.toml] +\[tool.mypy] +exclude = '''(?x)( + (^|/)[^/]*skipme_\.py$ + |(^|/)_skipme[^/]*\.py$ +)''' +[file x/__init__.py] +i: int = 0 +[file x/_skipme_please.py] +This isn't even syntatically valid! +[file x/please_skipme_.py] +Neither is this! + +[case testMultilineBasicExcludePyprojectTOML] +# cmd: mypy x +[file pyproject.toml] +\[tool.mypy] +exclude = """(?x)( + (^|/)[^/]*skipme_\\.py$ + |(^|/)_skipme[^/]*\\.py$ +)""" +[file x/__init__.py] +i: int = 0 +[file x/_skipme_please.py] +This isn't even syntatically valid! +[file x/please_skipme_.py] +Neither is this! + +[case testSequenceExcludePyprojectTOML] +# cmd: mypy x +[file pyproject.toml] +\[tool.mypy] +exclude = [ + '(^|/)[^/]*skipme_\.py$', # literal (no escaping) + "(^|/)_skipme[^/]*\\.py$", # basic (backslash needs escaping) +] +[file x/__init__.py] +i: int = 0 +[file x/_skipme_please.py] +This isn't even syntatically valid! +[file x/please_skipme_.py] +Neither is this!