psf / black

The uncompromising Python code formatter
https://black.readthedocs.io/en/stable/
MIT License
39.17k stars 2.47k forks source link

Documentation example `--extend-exclude ^/foo.py` does not exclude root file `foo.py` #2750

Open ketozhang opened 2 years ago

ketozhang commented 2 years ago

Describe the bug Black does not exclude the filefoo.py root when asked to exclude it with ^/foo.py. This particular example was used in the documentation:

extend-exclude = '''
# A regex preceded with ^/ will apply only to files and directories
# in the root of the project.
^/foo.py  # exclude a file named foo.py in the root of the project (in addition to the defaults)
'''

To Reproduce Start with an empty directory

$ echo "s='test'" > foo.py

$ black --extend-exclude "^/foo.py" --check .
would reformat foo.py
Oh no! 💥 💔 💥
1 file would be reformatted.

Same behavior using --exclude flag

Environment

ketozhang commented 2 years ago

However, this does work if placed in pyproject.toml. Weird

The proper regex should escape the period ^/foo\.py. Why is it that you need proper regex in commandline but not in the toml file?

MarcoGorelli commented 2 years ago

However, this does work if placed in pyproject.toml. Weird

Yes, because then black is able to correctly determine where the root of the project it. It would also work to do git init

In your example, root will be something like /, and the foo.py in your project will get normalised to something like /Users/marcogorelli/tmp-dir/foo.py and so won't be matched by the pattern you provided (^/foo.py).

ketozhang commented 2 years ago

Oh that's right. I'd mistakenly assume black would use CWD as root when .git/ or pyproject.toml is missing.

ketozhang commented 2 years ago

To be fair, there is is no indication of this in black --help. I was surprised ^/ even worked in the first place.

MarcoGorelli commented 2 years ago

To be fair, there is is no indication of this in black --help. I was surprised ^/ even worked in the first place.

Just a suggestion - do you want to add a note to the docs? e.g.

# A regex preceded with ^/ will apply only to files and directories
# in the root of the project.
^/foo.py  # exclude a file named foo.py in the root of the project (in addition to the defaults)
# see "Where Black looks for the file" for how black determines where the root is
'''
ketozhang commented 2 years ago

I don't think that particular modification would help since for the case of pyproject file, the root is always the path of the pyproject file.

A section on general examples of exclusion patterns will help:

Largely needed since everyone makes the mistake of attempting to use glob patterns (e.g., \.\/foo and ^foo\/ doesn't work as intended)

JosEnerv commented 2 years ago

extend-exclude does not do what I want it to do somehow : extend-exluded : src\/models

Verbose output :

src/models/something.py ignored: matches the --extend-exclude regular expression would reformat src/models/something.py

I don't get it.

tomschr commented 1 year ago

I had the same problem and circumvent it by using Git's .gitignore and adding my excludes:

# .gitignore file
/foo.py

If you want to ignore all Python files in the project's root dir (but not setup.py, for example) you could add these lines:

/*.py
!/setup.py

I've prepared a small environment to show how it looks like (see below for details). When I run black in verbose mode I get:

$ black --verbose --check .
Identified `/tmp/gitblack` as project root containing a .git directory.
Sources to be formatted: "."
.git ignored: matches the --exclude regular expression
foo.py ignored: matches the .gitignore file content
setup.py already well formatted, good job.
src/foo/foo.py already well formatted, good job.

All done! ✨ 🍰 ✨
2 files would be left unchanged.

Maybe this is not intended, but perhaps it's even better to have all the ignores in the .gitignore file anyway. If I don't want to reformat or check the source code of a Python file in the project's root directory, it shouldn't be included in the Git repo anyway.

Details about the test Git repo ``` $ mkdir -p /tmp/gitblack && cd /tmp/gitblack $ git init . $ mkdir -p src/foo/ $ touch foo.py src/foo/foo.py setup.py $ cat << EOF > .gitignore /*.py !/setup.py EOF $ git add .gitignore setup.py src/foo/foo.py $ git commit -m "Initial import" ```