black: Black stops looking for config at first pyproject.toml file even if there's no black section in it

Disclaimer: I was hesitant whether I should report this as a bug or rather a “Feature request”.

Describe the bug

In a project dealing with multiple sub-projects in a single repository (aka. mono-repo style) having more than one pyproject.toml file confuses black. In our example we wanted to have one top-level black configuration, while also having sub-project specific pyproject.toml files (without any black config) in each sub-directory. Unfortunately, running black on a sub-directory uses default configuration instead of the one defined in the top-level config file.

To Reproduce

Given this file structure:

.
|-- pyproject.toml
|-- sub1
|   |-- pyproject.toml
|   `-- test.py
`-- sub2
    `-- test.py

With the following contents:

==> ./pyproject.toml <==
[tool.black]
line-length = 20

==> ./sub1/pyproject.toml <==
[tool.isort]
profile = "black"

==> ./sub1/test.py <==
a_very_long_line = {"that should be wrapped": "to fit 20 characters line length"}

==> ./sub2/test.py <==
a_very_long_line = {"that should be wrapped": "to fit 20 characters line length"}

Running black on both test.py files:

$ black -v sub1/test.py
Identified `/private/tmp/black-many-configs/sub1` as project root containing a pyproject.toml.
Sources to be formatted: "test.py"
sub1/test.py already well formatted, good job.

All done! ✨ 🍰 ✨
1 file left unchanged.

$ black -v sub2/test.py
Identified `/private/tmp/black-many-configs` as project root containing a pyproject.toml.
Sources to be formatted: "sub2/test.py"
Using configuration from project root.
reformatted sub2/test.py

All done! ✨ 🍰 ✨
1 file reformatted.

Modifies only the file inside sub2 directory (the one without pyproject.toml file) even though sub1/pyproject.toml doesn’t contain any black config section.

Expected behavior

Both files should be modified and black should continue looking for a file that is relevant for its configuration (in this case, the one that has a [tool.black] section).

Environment

  • Black’s version: black, 22.1.0 (compiled: yes)
  • OS and Python version: OSX - Python 3.9.9

Additional context

A variation of this problem has already been reported in https://github.com/psf/black/issues/1826, where the proposed solution is to introduce support for the config option inside the sub-project’s pyproject.toml config file.

Other (potentially) relevant issues: https://github.com/psf/black/issues/2537, https://github.com/psf/black/issues/2850.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 10
  • Comments: 16 (5 by maintainers)

Commits related to this issue

Most upvoted comments

I feel we should only find the first pyproject.toml in the hierarchy and just error that there is no black config in the file we find. Then the user can fix their repo. I don’t think black should do anything clever here. I’m a big KISS (Keep it simple stupid) person. The second solution to allow specifying the config sounds a good middle ground.

Am I missing something here?

E.g. .flake8 does this, but is more explicit as it uses a non shared config file, but if an an empty / incomplete config file exists it will cause unexpected behaviors … I’m not saying this is ideal just comparing to other tools in the ecosystem

I also find it pretty problematic in a monorepo setup:

  • pre-commit hook doesn’t work as you can expect unless you provide --config flag explicitly, which can be really confusing (it took me a while to understand why files are formatted with no respect to the config), but when you specify it eventually - it works fine
  • VS Code is more problematic - here you also have to specify Python › Formatting: Black Args explicitly, but that’s more tricky because there’s no obvious way to share the config with your team - workspace-level settings.json is not universal, so you need to create sth like settings.json.default and tell people to include manually / with some custom script the settings specified there in their own config. That’s ofc doable, but it’s a pretty annoying experience for new-comers.

If Black would scan all the pyproject.toml files up to the root of a monorepo - nothing from the above would be needed. And empty Black section in pyproject.toml as a way to say “don’t scan further, just use Black defaults” should provide pretty smooth experience.

EDIT: I just had a look at #2525 - that should also be fine, I can handle specifying the path during project generation with cookiecutter then. It would be nice to be able to specify the path also from the project root, not only relatively, but I understand that would complicate the setup. @Shivansh-007 - is that PR expected to be merged anytime soon?

@Shivansh-007 I did a quick test with isort - if there’s intermediate pyproject.toml, but there’s no isort section there - it searches further and finally pick up the config from the root, so that’s exactly the behaviour we would like to see also in Black.

Looks like Where Black looks for the file does not make this special situation (multiple configs) better:

By default Black looks for pyproject.toml starting from the common base directory of all files and directories passed on the command line.

Had that issue just now. I would propose to

  • only search for a config starting from each file independently. This way it is always clear which file is chosen, independently how black is called (one or multiple files)
  • skip configs that do not contain a black tool section until one is found (in / 🙈 )