_Contents:_ **[Installation and usage](#installation-and-usage)** |
**[Code style](#the-black-code-style)** | **[Pragmatism](#pragmatism)** |
**[pyproject.toml](#pyprojecttoml)** | **[Editor integration](#editor-integration)** |
-**[blackd](#blackd)** | **[Version control integration](#version-control-integration)**
-| **[Ignoring unmodified files](#ignoring-unmodified-files)** | **[Used by](#used-by)**
-| **[Testimonials](#testimonials)** | **[Show your style](#show-your-style)** |
+**[blackd](#blackd)** | **[black-primer](#black-primer)** |
+**[Version control integration](#version-control-integration)** |
+**[Ignoring unmodified files](#ignoring-unmodified-files)** | **[Used by](#used-by)** |
+**[Testimonials](#testimonials)** | **[Show your style](#show-your-style)** |
**[Contributing](#contributing-to-black)** | **[Change Log](#change-log)** |
**[Authors](#authors)**
To get started right away with sensible defaults:
-```
+```sh
black {source_file_or_directory}
```
Actual details on _Black_ compatible configurations for various tools can be found in
[compatible_configs](https://github.com/psf/black/blob/master/docs/compatible_configs.md).
+### Migrating your code style without ruining git blame
+
+A long-standing argument against moving to automated code formatters like _Black_ is
+that the migration will clutter up the output of `git blame`. This was a valid argument,
+but since Git version 2.23, Git natively supports
+[ignoring revisions in blame](https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revltrevgt)
+with the `--ignore-rev` option. You can also pass a file listing the revisions to ignore
+using the `--ignore-revs-file` option. The changes made by the revision will be ignored
+when assigning blame. Lines modified by an ignored revision will be blamed on the
+previous revision that modified those lines.
+
+So when migrating your project's code style to _Black_, reformat everything and commit
+the changes (preferably in one massive commit). Then put the full 40 characters commit
+identifier(s) into a file.
+
+```
+# Migrate code style to Black
+5b4ab991dede475d393e9d69ec388fd6bd949699
+```
+
+Afterwards, you can pass that file to `git blame` and see clean and meaningful blame
+information.
+
+```console
+$ git blame important.py --ignore-revs-file .git-blame-ignore-revs
+7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 1) def very_important_function(text, file):
+abdfd8b0 (Alice Doe 2019-09-23 11:39:32 -0400 2) text = text.lstrip()
+7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 3) with open(file, "r+") as f:
+7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 4) f.write(formatted)
+```
+
+You can even configure `git` to automatically ignore revisions listed in a file on every
+call to `git blame`.
+
+```console
+$ git config blame.ignoreRevsFile .git-blame-ignore-revs
+```
+
+**The one caveat is that GitHub and GitLab do not yet support ignoring revisions using
+their native UI of blame.** So blame information will be cluttered with a reformatting
+commit on those platforms. (If you'd like this feature, there's an open issue for
+[GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/31423) and please let GitHub
+know!)
+
### NOTE: This is a beta product
_Black_ is already [successfully used](#used-by) by many projects, small and big. It
...
```
+_Black_ prefers parentheses over backslashes, and will remove backslashes if found.
+
+```py3
+# in:
+
+if some_short_rule1 \
+ and some_short_rule2:
+ ...
+
+# out:
+
+if some_short_rule1 and some_short_rule2:
+ ...
+
+
+# in:
+
+if some_long_rule1 \
+ and some_long_rule2:
+ ...
+
+# out:
+
+if (
+ some_long_rule1
+ and some_long_rule2
+):
+ ...
+
+```
+
+Backslashes and multiline strings are one of the two places in the Python grammar that
+break significant indentation. You never need backslashes, they are used to force the
+grammar to accept breaks that would otherwise be parse errors. That makes them confusing
+to look at and brittle to modify. This is why _Black_ always gets rid of them.
+
+If you're reaching for backslashes, that's a clear signal that you can do better if you
+slightly refactor your code. I hope some of the examples above show you that there are
+many ways in which you can do it.
+
+However there is one exception: `with` statements using multiple context managers.
+Python's grammar does not allow organizing parentheses around the series of context
+managers.
+
+We don't want formatting like:
+
+```py3
+with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
+ ... # nothing to split on - line too long
+```
+
+So _Black_ will now format it like this:
+
+```py3
+with \
+ make_context_manager(1) as cm1, \
+ make_context_manager(2) as cm2, \
+ make_context_manager(3) as cm3, \
+ make_context_manager(4) as cm4 \
+:
+ ... # backslashes and an ugly stranded colon
+```
+
You might have noticed that closing brackets are always dedented and that a trailing
comma is always added. Such formatting produces smaller diffs; when you add or remove an
element, it's always just one line. Also, having the closing bracket dedented provides a
[recommends](https://www.python.org/dev/peps/pep-0008/#whitespace-in-expressions-and-statements)
to treat `:` in slices as a binary operator with the lowest priority, and to leave an
equal amount of space on either side, except if a parameter is omitted (e.g.
-`ham[1 + 1 :]`). It also states that for extended slices, both `:` operators have to
-have the same amount of spacing, except if a parameter is omitted (`ham[1 + 1 ::]`).
-_Black_ enforces these rules consistently.
+`ham[1 + 1 :]`). It recommends no spaces around `:` operators for "simple expressions"
+(`ham[lower:upper]`), and extra space for "complex expressions"
+(`ham[lower : upper + offset]`). _Black_ treats anything more than variable names as
+"complex" (`ham[lower : upper + 1]`). It also states that for extended slices, both `:`
+operators have to have the same amount of spacing, except if a parameter is omitted
+(`ham[1 + 1 ::]`). _Black_ enforces these rules consistently.
This behaviour may raise `E203 whitespace before ':'` warnings in style guide
enforcement tools like Flake8. Since `E203` is not PEP 8 compliant, you should tell
6. Optionally, run _Black_ on every file save:
1. Make sure you have the
- [File Watcher](https://plugins.jetbrains.com/plugin/7177-file-watchers) plugin
+ [File Watchers](https://plugins.jetbrains.com/plugin/7177-file-watchers) plugin
installed.
2. Go to `Preferences or Settings -> Tools -> File Watchers` and click `+` to add a
new watcher:
- Output paths to refresh: `$FilePath$`
- Working directory: `$ProjectFileDir$`
- - Uncheck "Auto-save edited files to trigger the watcher"
+ - Uncheck "Auto-save edited files to trigger the watcher" in Advanced Options
### Wing IDE
### Kakoune
-Add the following hook to your kakrc, then run black with `:format`.
+Add the following hook to your kakrc, then run _Black_ with `:format`.
```
hook global WinSetOption filetype=python %{
There is no official blackd client tool (yet!). You can test that blackd is working
using `curl`:
-```
+```sh
blackd --bind-port 9090 & # or let blackd choose a port
curl -s -XPOST "localhost:9090" -d "print('valid')"
```
The response headers include a `X-Black-Version` header containing the version of
_Black_.
+## black-primer
+
+`black-primer` is a tool built for CI (and huumans) to have _Black_ `--check` a number
+of (configured in `primer.json`) Git accessible projects in parallel. _(A PR will be
+accepted to add Mercurial support.)_
+
+### Run flow
+
+- Ensure we have a `black` + `git` in PATH
+- Load projects from `primer.json`
+- Run projects in parallel with `--worker` workers (defaults to CPU count / 2)
+ - Checkout projects
+ - Run black and record result
+ - Clean up repository checkout _(can optionally be disabled via `--keep`)_
+- Display results summary to screen
+- Default to cleaning up `--work-dir` (which defaults to tempfile schemantics)
+- Return
+ - 0 for successful run
+ - < 0 for environment / internal error
+ - > 0 for each project with an error
+
+### Speed up Runs 🏎
+
+If you're running locally yourself to test black on lots of code try:
+
+- Using `-k` / `--keep` + `-w` / `--work-dir` so you don't have to re-checkout the repo
+ each run
+
+### CLI Arguments
+
+```text
+Usage: black-primer [OPTIONS]
+
+ primer - prime projects for blackening... 🏴
+
+Options:
+ -c, --config PATH JSON config file path [default: /Users/cooper/repos/
+ black/src/black_primer/primer.json]
+
+ --debug Turn on debug logging [default: False]
+ -k, --keep Keep workdir + repos post run [default: False]
+ -L, --long-checkouts Pull big projects to test [default: False]
+ -R, --rebase Rebase project if already checked out [default:
+ False]
+
+ -w, --workdir PATH Directory Path for repo checkouts [default: /var/fol
+ ders/tc/hbwxh76j1hn6gqjd2n2sjn4j9k1glp/T/primer.20200
+ 517125229]
+
+ -W, --workers INTEGER Number of parallel worker coroutines [default: 69]
+ -h, --help Show this message and exit.
+```
+
+### primer config file
+
+The config is JSON format. Its main element is the `"projects"` dictionary. Below
+explains each parameter:
+
+```json
+{
+ "projects": {
+ "00_Example": {
+ "cli_arguments": "List of extra CLI arguments to pass Black for this project",
+ "expect_formatting_changes": "Boolean to indicate that the version of Black is expected to cause changes",
+ "git_clone_url": "URL you would pass `git clone` to check out this repo",
+ "long_checkout": "Boolean to have repo skipped by defauult unless `--long-checkouts` is specified",
+ "py_versions": "List of major Python versions to run this project with - all will do as you'd expect - run on ALL versions"
+ },
+ "aioexabgp": {
+ "cli_arguments": [],
+ "expect_formatting_changes": true,
+ "git_clone_url": "https://github.com/cooperlees/aioexabgp.git",
+ "long_checkout": false,
+ "py_versions": ["all", "3.8"] // "all" ignores all other versions
+ }
+ }
+}
+```
+
+### Example run
+
+```console
+cooper-mbp:black cooper$ ~/venvs/b/bin/black-primer
+[2020-05-17 13:06:40,830] INFO: 4 projects to run Black over (lib.py:270)
+[2020-05-17 13:06:44,215] INFO: Analyzing results (lib.py:285)
+-- primer results 📊 --
+
+3 / 4 succeeded (75.0%) ✅
+1 / 4 FAILED (25.0%) 💩
+ - 0 projects disabled by config
+ - 0 projects skipped due to Python version
+ - 0 skipped due to long checkout
+
+Failed projects:
+
+## flake8-bugbear:
+ - Returned 1
+ - stdout:
+--- tests/b303_b304.py 2020-05-17 20:04:09.991227 +0000
++++ tests/b303_b304.py 2020-05-17 20:06:42.753851 +0000
+@@ -26,11 +26,11 @@
+ maxint = 5 # this is okay
+ # the following shouldn't crash
+ (a, b, c) = list(range(3))
+ # it's different than this
+ a, b, c = list(range(3))
+- a, b, c, = list(range(3))
++ a, b, c = list(range(3))
+ # and different than this
+ (a, b), c = list(range(3))
+ a, *b, c = [1, 2, 3, 4, 5]
+ b[1:3] = [0, 0]
+
+would reformat tests/b303_b304.py
+Oh no! 💥 💔 💥
+1 file would be reformatted, 22 files would be left unchanged.
+```
+
## Version control integration
Use [pre-commit](https://pre-commit.com/). Once you
example.
If you're already using Python 3.7, switch the `language_version` accordingly. Finally,
-`stable` is a tag that is pinned to the latest release on PyPI. If you'd rather run on
+`stable` is a branch that tracks the latest release on PyPI. If you'd rather run on
master, this is also an option.
## Ignoring unmodified files
The following notable open-source projects trust _Black_ with enforcing a consistent
code style: pytest, tox, Pyramid, Django Channels, Hypothesis, attrs, SQLAlchemy,
-Poetry, PyPA applications (Warehouse, Pipenv, virtualenv), pandas, Pillow, every Datadog
-Agent Integration, Home Assistant.
+Poetry, PyPA applications (Warehouse, Bandersnatch, Pipenv, virtualenv), pandas, Pillow,
+every Datadog Agent Integration, Home Assistant.
-The following organizations use _Black_: Dropbox.
+The following organizations use _Black_: Facebook, Dropbox.
Are we missing anyone? Let us know.