Search code examples
pythonvisual-studio-codepyproject.tomlpython-black

Black not respecting extend-exclude in pyproject.toml


In VSCode, with Python 3.9 and black==22.6.0, I have a project structure like:

--- root
------src
---------module0.py
---------module1.py
------tests
---------test_folder0
------------test_file0.py
------------test_file1.py
---------test_folder1
---------etc.

In pyproject.toml I can't get the extend-exlude part to actually exclude my test files. I've tried multiple different ways, both for the entire tests folder as well as for the test_whatever.py files but nothing seems to work, even though my various attempts have been validated by https://regex101.com/. The simplest example:

[tool.black]
line-length = 200
target-version = ['py39']
include = '\.pyi?$'
extend-exclude = '''.*test_.*'''

Either my regex is wrong (or black requires some modifications) or VSCode is ignoring my project's configuration or idk.


Solution

  • Maintainer of Black here :wave:

    OK, so I actually missed a few points in my comments. To address the main question, this is 100% expected behaviour. Your regex is fine.

    The thing is that when you ask VSCode to format your file on save, it calls Black passing the filepath to your current file (you just saved) directly. If you open the "Output" tab in the bottom panel and then save a Python file, you'll notice something like this:

    ./venv/bin/python -m black --safe --diff --quiet ./tests/data/nothing-changed.py
    

    --include, --exclude, and --extend-exclude only affect files and directories that are discovered by Black itself. You might be wondering, huh, Black can look for files to format? Yes, it does when you run black . or give Black any other directory. The flipside of this is that these options do nothing to files given as an argument to Black.

    If you want to keep Format on Save enabled, your only recourse is to use --force-exclude which is similar to --extend-exclude but it's always enforced. You can either configure --force-exclude in pyproject.toml or via VSCode's Black arguments setting (preferably for the current workspace only).

    The difference between putting it in pyproject.toml and configuring VSCode to pass extra options to Black is well, when it's applied. If it's in pyproject.toml it will always be enforced, even when you're not using VSCode and instead are using a bash shell or whatever. This can be useful if you're using pre-commit (which passes files to Black directly just like VSCode) or similar, but can be annoying otherwise.

    (if you do choose to force exclude project-wide via pyproject.toml, you can format force-excluded files by either piping it in or temporarily clearing --force-exclude on the CLI, e.g. black --force-exclude='' file_you_want_to_format_even_though_it_is_force_excluded.py)