Skip to content

Commit

Permalink
Preserves interpreting multi-line strings as a single value for `excl…
Browse files Browse the repository at this point in the history
…ude` in TOML files (#11828)

Multiple regexes are expressed as a sequence.

Fixes #11825.

Co-authored-by: Matthew W <[email protected]>
Co-authored-by: Shantanu <[email protected]>
  • Loading branch information
3 people authored and JukkaL committed Jan 5, 2022
1 parent d4bda9a commit 4a344ed
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 0 deletions.
35 changes: 35 additions & 0 deletions docs/source/config_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
***************************

Expand Down Expand Up @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions mypy/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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,
})


Expand Down
45 changes: 45 additions & 0 deletions test-data/unit/cmdline.pyproject.test
Original file line number Diff line number Diff line change
Expand Up @@ -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!

0 comments on commit 4a344ed

Please sign in to comment.