Compare commits

..

No commits in common. "main" and "21.8b0" have entirely different histories.
main ... 21.8b0

397 changed files with 9168 additions and 27048 deletions

9
.coveragerc Normal file
View File

@ -0,0 +1,9 @@
[report]
omit =
src/blib2to3/*
tests/data/*
*/site-packages/*
.tox/*
[run]
relative_files = True

View File

@ -1,8 +1,7 @@
[flake8] [flake8]
# B905 should be enabled when we drop support for 3.9 ignore = E203, E266, E501, W503
ignore = E203, E266, E501, E701, E704, W503, B905, B907
# line length is intentionally set to 80 here because black uses Bugbear # line length is intentionally set to 80 here because black uses Bugbear
# See https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#bugbear for more details # See https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length for more details
max-line-length = 80 max-line-length = 80
max-complexity = 18 max-complexity = 18
select = B,C,E,F,W,T4,B9 select = B,C,E,F,W,T4,B9

View File

@ -1,3 +0,0 @@
node: $Format:%H$
node-date: $Format:%cI$
describe-name: $Format:%(describe:tags=true,match=[0-9]*)$

2
.gitattributes vendored
View File

@ -1,2 +0,0 @@
.git_archival.txt export-subst
*.py diff=python

View File

@ -6,61 +6,42 @@ labels: "T: bug"
assignees: "" assignees: ""
--- ---
<!--
Please make sure that the bug is not already fixed either in newer versions or the
current development version. To confirm this, you have three options:
1. Update Black's version if a newer release exists: `pip install -U black`
2. Use the online formatter at <https://black.vercel.app/?version=main>, which will use
the latest main branch. Note that the online formatter currently runs on
an older version of Python and may not support newer syntax, such as the
extended f-string syntax added in Python 3.12.
3. Or run _Black_ on your machine:
- create a new virtualenv (make sure it's the same Python version);
- clone this repository;
- run `pip install -e .[d]`;
- run `pip install -r test_requirements.txt`
- make sure it's sane by running `python -m pytest`; and
- run `black` like you did last time.
-->
**Describe the bug** **Describe the bug**
<!-- A clear and concise description of what the bug is. --> <!-- A clear and concise description of what the bug is. -->
**To Reproduce** **To Reproduce**
<!-- <!-- Steps to reproduce the behavior:
Minimal steps to reproduce the behavior with source code and Black's configuration.
-->
For example, take this code: For example:
1. Take this file '...'
```python 1. Run _Black_ on it with these arguments '...'
this = "code" 1. See error -->
```
And run it with these arguments:
```sh
$ black file.py --target-version py39
```
The resulting error is:
> cannot format file.py: INTERNAL ERROR: ...
**Expected behavior** **Expected behavior**
<!-- A clear and concise description of what you expected to happen. --> <!-- A clear and concise description of what you expected to happen. -->
**Environment** **Environment (please complete the following information):**
<!-- Please complete the following information: --> - Version: <!-- e.g. [main] -->
- Black's version: <!-- e.g. [main] -->
- OS and Python version: <!-- e.g. [Linux/Python 3.7.4rc1] --> - OS and Python version: <!-- e.g. [Linux/Python 3.7.4rc1] -->
**Does this bug also happen on main?**
<!-- To answer this, you have two options:
1. Use the online formatter at <https://black.vercel.app/?version=main>, which will use
the latest main branch.
1. Or run _Black_ on your machine:
- create a new virtualenv (make sure it's the same Python version);
- clone this repository;
- run `pip install -e .[d,python2]`;
- run `pip install -r test_requirements.txt`
- make sure it's sane by running `python -m pytest`; and
- run `black` like you did last time. -->
**Additional context** **Additional context**
<!-- Add any other context about the problem here. --> <!-- Add any other context about the problem here. -->

View File

@ -1,8 +1,8 @@
--- ---
name: Code style issue name: Style issue
about: Help us improve the Black code style about: Help us improve the Black style
title: "" title: ""
labels: "T: style" labels: "T: design"
assignees: "" assignees: ""
--- ---

View File

@ -18,9 +18,9 @@
Tests are required for bugfixes and new features. Documentation changes Tests are required for bugfixes and new features. Documentation changes
are necessary for formatting and most enhancement changes. --> are necessary for formatting and most enhancement changes. -->
- [ ] Add an entry in `CHANGES.md` if necessary? - [] Add a CHANGELOG entry if necessary?
- [ ] Add / update tests if necessary? - [] Add / update tests if necessary?
- [ ] Add new / update outdated documentation? - [] Add new / update outdated documentation?
<!-- Just as a reminder, everyone in all psf/black spaces including PRs <!-- Just as a reminder, everyone in all psf/black spaces including PRs
must follow the PSF Code of Conduct (link below). must follow the PSF Code of Conduct (link below).

View File

@ -1,16 +0,0 @@
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions"
# Workflow files in .github/workflows will be checked
directory: "/"
schedule:
interval: "weekly"
labels: ["skip news", "C: dependencies"]
- package-ecosystem: "pip"
directory: "docs/"
schedule:
interval: "weekly"
labels: ["skip news", "C: dependencies", "T: documentation"]

View File

@ -4,9 +4,6 @@ on:
pull_request: pull_request:
types: [opened, synchronize, labeled, unlabeled, reopened] types: [opened, synchronize, labeled, unlabeled, reopened]
permissions:
contents: read
jobs: jobs:
build: build:
name: Changelog Entry Check name: Changelog Entry Check
@ -14,11 +11,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Grep CHANGES.md for PR number - name: Grep CHANGES.md for PR number
if: contains(github.event.pull_request.labels.*.name, 'skip news') != true if: contains(github.event.pull_request.labels.*.name, 'skip news') != true
run: | run: |
grep -Pz "\((\n\s*)?#${{ github.event.pull_request.number }}(\n\s*)?\)" CHANGES.md || \ grep -Pz "\((\n\s*)?#${{ github.event.pull_request.number }}(\n\s*)?\)" CHANGES.md || \
(echo "Please add '(#${{ github.event.pull_request.number }})' change line to CHANGES.md (or if appropriate, ask a maintainer to add the 'skip news' label)" && \ (echo "Please add '(#${{ github.event.pull_request.number }})' change line to CHANGES.md" && \
exit 1) exit 1)

View File

@ -1,155 +0,0 @@
name: diff-shades
on:
push:
branches: [main]
paths: ["src/**", "pyproject.toml", ".github/workflows/*"]
pull_request:
paths: ["src/**", "pyproject.toml", ".github/workflows/*"]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
jobs:
configure:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-config.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install diff-shades and support dependencies
run: |
python -m pip install 'click>=8.1.7' packaging urllib3
python -m pip install https://github.com/ichard26/diff-shades/archive/stable.zip
- name: Calculate run configuration & metadata
id: set-config
env:
GITHUB_TOKEN: ${{ github.token }}
run: >
python scripts/diff_shades_gha_helper.py config ${{ github.event_name }}
${{ matrix.mode }}
analysis:
name: analysis / ${{ matrix.mode }}
needs: configure
runs-on: ubuntu-latest
env:
HATCH_BUILD_HOOKS_ENABLE: "1"
# Clang is less picky with the C code it's given than gcc (and may
# generate faster binaries too).
CC: clang-18
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.configure.outputs.matrix) }}
steps:
- name: Checkout this repository (full clone)
uses: actions/checkout@v4
with:
# The baseline revision could be rather old so a full clone is ideal.
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install diff-shades and support dependencies
run: |
python -m pip install https://github.com/ichard26/diff-shades/archive/stable.zip
python -m pip install 'click>=8.1.7' packaging urllib3
# After checking out old revisions, this might not exist so we'll use a copy.
cat scripts/diff_shades_gha_helper.py > helper.py
git config user.name "diff-shades-gha"
git config user.email "diff-shades-gha@example.com"
- name: Attempt to use cached baseline analysis
id: baseline-cache
uses: actions/cache@v4
with:
path: ${{ matrix.baseline-analysis }}
key: ${{ matrix.baseline-cache-key }}
- name: Build and install baseline revision
if: steps.baseline-cache.outputs.cache-hit != 'true'
env:
GITHUB_TOKEN: ${{ github.token }}
run: >
${{ matrix.baseline-setup-cmd }}
&& python -m pip install .
- name: Analyze baseline revision
if: steps.baseline-cache.outputs.cache-hit != 'true'
run: >
diff-shades analyze -v --work-dir projects-cache/
${{ matrix.baseline-analysis }} ${{ matrix.force-flag }}
- name: Build and install target revision
env:
GITHUB_TOKEN: ${{ github.token }}
run: >
${{ matrix.target-setup-cmd }}
&& python -m pip install .
- name: Analyze target revision
run: >
diff-shades analyze -v --work-dir projects-cache/
${{ matrix.target-analysis }} --repeat-projects-from
${{ matrix.baseline-analysis }} ${{ matrix.force-flag }}
- name: Generate HTML diff report
run: >
diff-shades --dump-html diff.html compare --diff
${{ matrix.baseline-analysis }} ${{ matrix.target-analysis }}
- name: Upload diff report
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.mode }}-diff.html
path: diff.html
- name: Upload baseline analysis
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.baseline-analysis }}
path: ${{ matrix.baseline-analysis }}
- name: Upload target analysis
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.target-analysis }}
path: ${{ matrix.target-analysis }}
- name: Generate summary file (PR only)
if: github.event_name == 'pull_request' && matrix.mode == 'preview-changes'
run: >
python helper.py comment-body ${{ matrix.baseline-analysis }}
${{ matrix.target-analysis }} ${{ matrix.baseline-sha }}
${{ matrix.target-sha }} ${{ github.event.pull_request.number }}
- name: Upload summary file (PR only)
if: github.event_name == 'pull_request' && matrix.mode == 'preview-changes'
uses: actions/upload-artifact@v4
with:
name: .pr-comment.json
path: .pr-comment.json
- name: Verify zero changes (PR only)
if: matrix.mode == 'assert-no-changes'
run: >
diff-shades compare --check ${{ matrix.baseline-analysis }} ${{ matrix.target-analysis }}
|| (echo "Please verify you didn't change the stable code style unintentionally!" && exit 1)
- name: Check for failed files for target revision
# Even if the previous step failed, we should still check for failed files.
if: always()
run: >
diff-shades show-failed --check --show-log ${{ matrix.target-analysis }}

View File

@ -1,49 +0,0 @@
name: diff-shades-comment
on:
workflow_run:
workflows: [diff-shades]
types: [completed]
permissions:
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "*"
- name: Install support dependencies
run: |
python -m pip install pip --upgrade
python -m pip install click packaging urllib3
- name: Get details from initial workflow run
id: metadata
env:
GITHUB_TOKEN: ${{ github.token }}
run: >
python scripts/diff_shades_gha_helper.py comment-details
${{github.event.workflow_run.id }}
- name: Try to find pre-existing PR comment
if: steps.metadata.outputs.needs-comment == 'true'
id: find-comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e
with:
issue-number: ${{ steps.metadata.outputs.pr-number }}
comment-author: "github-actions[bot]"
body-includes: "diff-shades"
- name: Create or update PR comment
if: steps.metadata.outputs.needs-comment == 'true'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ steps.metadata.outputs.pr-number }}
body: ${{ steps.metadata.outputs.comment-body }}
edit-mode: replace

View File

@ -1,10 +1,7 @@
name: Documentation name: Documentation Build
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
# We want to run on external PRs, but not on our own internal PRs as they'll be run # We want to run on external PRs, but not on our own internal PRs as they'll be run
@ -21,20 +18,16 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up latest Python - name: Set up latest Python
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with:
python-version: "3.13"
allow-prereleases: true
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install uv python -m pip install --upgrade pip setuptools wheel
python -m uv venv python -m pip install -e ".[d]"
python -m uv pip install -e ".[d]" python -m pip install -r "docs/requirements.txt"
python -m uv pip install -r "docs/requirements.txt"
- name: Build documentation - name: Build documentation
run: sphinx-build -a -b html -W --keep-going docs/ docs/_build run: sphinx-build -a -b html -W --keep-going docs/ docs/_build

View File

@ -5,10 +5,7 @@ on:
branches: branches:
- "main" - "main"
release: release:
types: [published] types: created
permissions:
contents: read
jobs: jobs:
docker: docker:
@ -16,16 +13,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v1
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v3 uses: docker/login-action@v1
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
@ -36,7 +33,7 @@ jobs:
latest_non_release)" >> $GITHUB_ENV latest_non_release)" >> $GITHUB_ENV
- name: Build and push - name: Build and push
uses: docker/build-push-action@v6 uses: docker/build-push-action@v2
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
@ -44,26 +41,13 @@ jobs:
tags: pyfound/black:latest,pyfound/black:${{ env.GIT_TAG }} tags: pyfound/black:latest,pyfound/black:${{ env.GIT_TAG }}
- name: Build and push latest_release tag - name: Build and push latest_release tag
if: if: ${{ github.event_name == 'release' && github.event.action == 'created' }}
${{ github.event_name == 'release' && github.event.action == 'published' && uses: docker/build-push-action@v2
!github.event.release.prerelease }}
uses: docker/build-push-action@v6
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: pyfound/black:latest_release tags: pyfound/black:latest_release
- name: Build and push latest_prerelease tag
if:
${{ github.event_name == 'release' && github.event.action == 'published' &&
github.event.release.prerelease }}
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: pyfound/black:latest_prerelease
- name: Image digest - name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }} run: echo ${{ steps.docker_build.outputs.digest }}

View File

@ -2,13 +2,6 @@ name: Fuzz
on: [push, pull_request] on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
jobs: jobs:
build: build:
# We want to run on external PRs, but not on our own internal PRs as they'll be run # We want to run on external PRs, but not on our own internal PRs as they'll be run
@ -22,16 +15,15 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.9", "3.10", "3.11", "3.12.4", "3.13"] python-version: ["3.6", "3.7", "3.8", "3.9", "3.10-dev"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install dependencies - name: Install dependencies
run: | run: |

View File

@ -1,4 +1,4 @@
name: Lint + format ourselves name: Lint
on: [push, pull_request] on: [push, pull_request]
@ -14,35 +14,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Assert PR target is main - name: Set up Python
if: github.event_name == 'pull_request' && github.repository == 'psf/black' uses: actions/setup-python@v2
run: |
if [ "$GITHUB_BASE_REF" != "main" ]; then
echo "::error::PR targeting '$GITHUB_BASE_REF', please refile targeting 'main'." && exit 1
fi
- name: Set up latest Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
allow-prereleases: true
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
python -m pip install -e '.' python -m pip install -e '.[d]'
python -m pip install tox
- name: Run pre-commit hooks - name: Lint
uses: pre-commit/action@v3.0.1 uses: pre-commit/action@v2.0.2
- name: Format ourselves
run: |
tox -e run_self
- name: Regenerate schema
run: |
tox -e generate_schema
git diff --exit-code

47
.github/workflows/primer.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Primer
on:
push:
paths-ignore:
- "docs/**"
- "*.md"
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
jobs:
build:
# We want to run on external PRs, but not on our own internal PRs as they'll be run
# by the push to the branch. Without this if check, checks are duplicated since
# internal PRs match both the push and pull_request events.
if:
github.event_name == 'push' || github.event.pull_request.head.repo.full_name !=
github.repository
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10-dev"]
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[d,jupyter]"
- name: Primer run
env:
pythonioencoding: utf-8
run: |
black-primer

View File

@ -1,130 +1,31 @@
name: Build and publish name: pypi_upload
on: on:
release: release:
types: [published] types: created
pull_request:
push:
branches:
- main
permissions:
contents: read
jobs: jobs:
main: build:
name: sdist + pure wheel name: PyPI Upload
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'release'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up latest Python - name: Set up Python
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with:
python-version: "3.13"
allow-prereleases: true
- name: Install latest pip, build, twine - name: Install latest pip, setuptools, twine + wheel
run: | run: |
python -m pip install --upgrade --disable-pip-version-check pip python -m pip install --upgrade pip setuptools twine wheel
python -m pip install --upgrade build twine
- name: Build wheel and source distributions - name: Build wheels
run: python -m build run: |
python setup.py bdist_wheel
python setup.py sdist
- if: github.event_name == 'release' - name: Upload to PyPI via Twine
name: Upload to PyPI via Twine
env: env:
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: twine upload --verbose -u '__token__' dist/*
generate_wheels_matrix:
name: generate wheels matrix
runs-on: ubuntu-latest
outputs:
include: ${{ steps.set-matrix.outputs.include }}
steps:
- uses: actions/checkout@v4
# Keep cibuildwheel version in sync with below
- name: Install cibuildwheel and pypyp
run: | run: |
pipx install cibuildwheel==2.22.0 twine upload --verbose -u '__token__' dist/*
pipx install pypyp==1.3.0
- name: generate matrix
if: github.event_name != 'pull_request'
run: |
{
cibuildwheel --print-build-identifiers --platform linux \
| pyp 'json.dumps({"only": x, "os": "ubuntu-latest"})' \
&& cibuildwheel --print-build-identifiers --platform macos \
| pyp 'json.dumps({"only": x, "os": "macos-latest"})' \
&& cibuildwheel --print-build-identifiers --platform windows \
| pyp 'json.dumps({"only": x, "os": "windows-latest"})'
} | pyp 'json.dumps(list(map(json.loads, lines)))' > /tmp/matrix
env:
CIBW_ARCHS_LINUX: x86_64
CIBW_ARCHS_MACOS: x86_64 arm64
CIBW_ARCHS_WINDOWS: AMD64
- name: generate matrix (PR)
if: github.event_name == 'pull_request'
run: |
{
cibuildwheel --print-build-identifiers --platform linux \
| pyp 'json.dumps({"only": x, "os": "ubuntu-latest"})'
} | pyp 'json.dumps(list(map(json.loads, lines)))' > /tmp/matrix
env:
CIBW_BUILD: "cp39-* cp313-*"
CIBW_ARCHS_LINUX: x86_64
- id: set-matrix
run: echo "include=$(cat /tmp/matrix)" | tee -a $GITHUB_OUTPUT
mypyc:
name: mypyc wheels ${{ matrix.only }}
needs: generate_wheels_matrix
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.generate_wheels_matrix.outputs.include) }}
steps:
- uses: actions/checkout@v4
# Keep cibuildwheel version in sync with above
- uses: pypa/cibuildwheel@v2.23.3
with:
only: ${{ matrix.only }}
- name: Upload wheels as workflow artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.only }}-mypyc-wheels
path: ./wheelhouse/*.whl
- if: github.event_name == 'release'
name: Upload wheels to PyPI via Twine
env:
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: pipx run twine upload --verbose -u '__token__' wheelhouse/*.whl
update-stable-branch:
name: Update stable branch
needs: [main, mypyc]
runs-on: ubuntu-latest
if: github.event_name == 'release'
permissions:
contents: write
steps:
- name: Checkout stable branch
uses: actions/checkout@v4
with:
ref: stable
fetch-depth: 0
- if: github.event_name == 'release'
name: Update stable branch to release tag & push
run: |
git reset --hard ${{ github.event.release.tag_name }}
git push

View File

@ -1,56 +0,0 @@
name: Release tool CI
on:
push:
paths:
- .github/workflows/release_tests.yml
- release.py
- release_tests.py
pull_request:
paths:
- .github/workflows/release_tests.yml
- release.py
- release_tests.py
jobs:
build:
# We want to run on external PRs, but not on our own internal PRs as they'll be run
# by the push to the branch. Without this if check, checks are duplicated since
# internal PRs match both the push and pull_request events.
if:
github.event_name == 'push' || github.event.pull_request.head.repo.full_name !=
github.repository
name: Running python ${{ matrix.python-version }} on ${{matrix.os}}
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ["3.13"]
os: [macOS-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v4
with:
# Give us all history, branches and tags
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Print Python Version
run: python --version --version && which python
- name: Print Git Version
run: git --version && which git
- name: Update pip, setuptools + wheels
run: |
python -m pip install --upgrade pip setuptools wheel
- name: Run unit tests via coverage + print report
run: |
python -m pip install coverage
coverage run scripts/release_tests.py
coverage report --show-missing

View File

@ -11,15 +11,8 @@ on:
- "docs/**" - "docs/**"
- "*.md" - "*.md"
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
jobs: jobs:
main: build:
# We want to run on external PRs, but not on our own internal PRs as they'll be run # We want to run on external PRs, but not on our own internal PRs as they'll be run
# by the push to the branch. Without this if check, checks are duplicated since # by the push to the branch. Without this if check, checks are duplicated since
# internal PRs match both the push and pull_request events. # internal PRs match both the push and pull_request events.
@ -31,39 +24,35 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.9", "3.10", "3.11", "3.12.4", "3.13", "pypy-3.9"] python-version: ["3.6", "3.7", "3.8", "3.9", "3.10-dev"]
os: [ubuntu-latest, macOS-latest, windows-latest] os: [ubuntu-latest, macOS-latest, windows-latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install tox - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
python -m pip install --upgrade tox python -m pip install --upgrade tox
- name: Unit tests - name: Unit tests
if: "!startsWith(matrix.python-version, 'pypy')" run: |
run: tox -e ci-py -- -v --color=yes
tox -e ci-py$(echo ${{ matrix.python-version }} | tr -d '.') -- -v --color=yes
- name: Unit tests (pypy) - name: Publish coverage to Coveralls
if: "startsWith(matrix.python-version, 'pypy')" # If pushed / is a pull request against main repo AND
run: tox -e ci-pypy3 -- -v --color=yes
- name: Upload coverage to Coveralls
# Upload coverage if we are on the main repository and
# we're running on Linux (this action only supports Linux) # we're running on Linux (this action only supports Linux)
if: if:
github.repository == 'psf/black' && matrix.os == 'ubuntu-latest' && ((github.event_name == 'push' && github.repository == 'psf/black') ||
!startsWith(matrix.python-version, 'pypy') github.event.pull_request.base.repo.full_name == 'psf/black') && matrix.os ==
uses: AndreMiras/coveralls-python-action@ac868b9540fad490f7ca82b8ca00480fd751ed19 'ubuntu-latest'
uses: AndreMiras/coveralls-python-action@v20201129
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
parallel: true parallel: true
@ -71,40 +60,17 @@ jobs:
debug: true debug: true
coveralls-finish: coveralls-finish:
needs: main needs: build
if: github.repository == 'psf/black' # If pushed / is a pull request against main repo
if:
(github.event_name == 'push' && github.repository == 'psf/black') ||
github.event.pull_request.base.repo.full_name == 'psf/black'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Send finished signal to Coveralls - name: Coveralls finished
uses: AndreMiras/coveralls-python-action@ac868b9540fad490f7ca82b8ca00480fd751ed19 uses: AndreMiras/coveralls-python-action@v20201129
with: with:
parallel-finished: true parallel-finished: true
debug: true debug: true
uvloop:
if:
github.event_name == 'push' || github.event.pull_request.head.repo.full_name !=
github.repository
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest]
steps:
- uses: actions/checkout@v4
- name: Set up latest Python
uses: actions/setup-python@v5
with:
python-version: "3.12.4"
- name: Install black with uvloop
run: |
python -m pip install pip --upgrade --disable-pip-version-check
python -m pip install -e ".[uvloop]"
- name: Format ourselves
run: python -m black --check src/ tests/

View File

@ -1,25 +1,22 @@
name: Publish executables name: Upload self-contained binaries
on: on:
release: release:
types: [published] types: [published]
permissions:
contents: write # actions/upload-release-asset needs this.
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [windows-2019, ubuntu-22.04, macos-latest] os: [windows-2019, ubuntu-20.04, macos-latest]
include: include:
- os: windows-2019 - os: windows-2019
pathsep: ";" pathsep: ";"
asset_name: black_windows.exe asset_name: black_windows.exe
executable_mime: "application/vnd.microsoft.portable-executable" executable_mime: "application/vnd.microsoft.portable-executable"
- os: ubuntu-22.04 - os: ubuntu-20.04
pathsep: ":" pathsep: ":"
asset_name: black_linux asset_name: black_linux
executable_mime: "application/x-executable" executable_mime: "application/x-executable"
@ -29,28 +26,22 @@ jobs:
executable_mime: "application/x-mach-binary" executable_mime: "application/x-mach-binary"
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: Set up latest Python - name: Set up latest Python
uses: actions/setup-python@v5 uses: actions/setup-python@v2
with: with:
python-version: "3.12.4" python-version: "*"
- name: Install Black and PyInstaller - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip wheel python -m pip install --upgrade pip wheel setuptools
python -m pip install .[colorama] python -m pip install .
python -m pip install pyinstaller python -m pip install pyinstaller
- name: Build executable with PyInstaller - name: Build binary
run: >
python -m PyInstaller -F --name ${{ matrix.asset_name }} --add-data
'src/blib2to3${{ matrix.pathsep }}blib2to3' src/black/__main__.py
- name: Quickly test executable
run: | run: |
./dist/${{ matrix.asset_name }} --version python -m PyInstaller -F --name ${{ matrix.asset_name }} --add-data 'src/blib2to3${{ matrix.pathsep }}blib2to3' src/black/__main__.py
./dist/${{ matrix.asset_name }} src --verbose
- name: Upload binary as release asset - name: Upload binary as release asset
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1

45
.github/workflows/uvloop_test.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: test uvloop
on:
push:
paths-ignore:
- "docs/**"
- "*.md"
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
jobs:
build:
# We want to run on external PRs, but not on our own internal PRs as they'll be run
# by the push to the branch. Without this if check, checks are duplicated since
# internal PRs match both the push and pull_request events.
if:
github.event_name == 'push' || github.event.pull_request.head.repo.full_name !=
github.repository
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
- name: Install latest pip
run: |
python -m pip install --upgrade pip
- name: Test uvloop Extra Install
run: |
python -m pip install -e ".[uvloop]"
- name: Primer uvloop run
run: |
black-primer

9
.gitignore vendored
View File

@ -4,25 +4,18 @@
_build _build
.DS_Store .DS_Store
.vscode .vscode
.python-version
docs/_static/pypi.svg docs/_static/pypi.svg
.tox .tox
__pycache__ __pycache__
# Packaging artifacts
black.egg-info black.egg-info
black.dist-info
build/ build/
dist/ dist/
pip-wheel-metadata/ pip-wheel-metadata/
.eggs
src/_black_version.py src/_black_version.py
.idea .idea
.eggs
.dmypy.json .dmypy.json
*.swp *.swp
.hypothesis/ .hypothesis/
venv/ venv/
.ipynb_checkpoints/ .ipynb_checkpoints/
node_modules/

View File

@ -1,83 +1,52 @@
# Note: don't use this config for your own repositories. Instead, see # Note: don't use this config for your own repositories. Instead, see
# "Version control integration" in docs/integrations/source_version_control.md # "Version control integration" in docs/integrations/source_version_control.md
exclude: ^(profiling/|tests/data/) exclude: ^(src/blib2to3/|profiling/|tests/data/)
repos: repos:
- repo: local - repo: local
hooks: hooks:
- id: black
name: black
language: system
entry: black
minimum_pre_commit_version: 2.9.2
require_serial: true
types_or: [python, pyi]
- id: check-pre-commit-rev-in-example - id: check-pre-commit-rev-in-example
name: Check pre-commit rev in example name: Check pre-commit rev in example
language: python language: python
entry: python -m scripts.check_pre_commit_rev_in_example entry: python -m scripts.check_pre_commit_rev_in_example
files: '(CHANGES\.md|source_version_control\.md)$' files: '(CHANGES\.md|source_version_control\.md)$'
additional_dependencies: additional_dependencies:
&version_check_dependencies [ ["commonmark==0.9.1", "pyyaml==5.4.1", "beautifulsoup4==4.9.3"]
commonmark==0.9.1,
pyyaml==6.0.1,
beautifulsoup4==4.9.3,
]
- id: check-version-in-the-basics-example - repo: https://gitlab.com/pycqa/flake8
name: Check black version in the basics example rev: 3.9.2
language: python
entry: python -m scripts.check_version_in_basics_example
files: '(CHANGES\.md|the_basics\.md)$'
additional_dependencies: *version_check_dependencies
- repo: https://github.com/pycqa/isort
rev: 6.0.1
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 7.2.0
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: additional_dependencies: [flake8-bugbear]
- flake8-bugbear==24.2.6
- flake8-comprehensions
- flake8-simplify
exclude: ^src/blib2to3/
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.15.0 rev: v0.910
hooks: hooks:
- id: mypy - id: mypy
exclude: ^(docs/conf.py|scripts/generate_schema.py)$ exclude: ^docs/conf.py
args: [] additional_dependencies:
additional_dependencies: &mypy_deps - types-dataclasses >= 0.1.3
- types-PyYAML - types-PyYAML
- types-atheris
- tomli >= 0.2.6, < 2.0.0 - tomli >= 0.2.6, < 2.0.0
- click >= 8.2.0 - types-typed-ast >= 1.4.1
# Click is intentionally out-of-sync with pyproject.toml - click >= 8.0.0
# v8.2 has breaking changes. We work around them at runtime, but we need the newer stubs.
- packaging >= 22.0
- platformdirs >= 2.1.0 - platformdirs >= 2.1.0
- pytokens >= 0.1.10
- pytest
- hypothesis
- aiohttp >= 3.7.4
- types-commonmark
- urllib3
- hypothesmith
- id: mypy
name: mypy (Python 3.10)
files: scripts/generate_schema.py
args: ["--python-version=3.10"]
additional_dependencies: *mypy_deps
- repo: https://github.com/rbubley/mirrors-prettier - repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.5.3 rev: v2.3.2
hooks: hooks:
- id: prettier - id: prettier
types_or: [markdown, yaml, json] exclude: ^Pipfile\.lock
exclude: \.github/workflows/diff_shades\.yml
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v4.0.1
hooks: hooks:
- id: end-of-file-fixer - id: end-of-file-fixer
- id: trailing-whitespace - id: trailing-whitespace
ci:
autoupdate_schedule: quarterly

View File

@ -1,5 +1,3 @@
# Note that we recommend using https://github.com/psf/black-pre-commit-mirror instead
# This will work about 2x as fast as using the hooks in this repository
- id: black - id: black
name: black name: black
description: "Black: The uncompromising Python code formatter" description: "Black: The uncompromising Python code formatter"

View File

@ -3,12 +3,8 @@ version: 2
formats: formats:
- htmlzip - htmlzip
build:
os: ubuntu-22.04
tools:
python: "3.11"
python: python:
version: 3.8
install: install:
- requirements: docs/requirements.txt - requirements: docs/requirements.txt
@ -16,6 +12,3 @@ python:
path: . path: .
extra_requirements: extra_requirements:
- d - d
sphinx:
configuration: docs/conf.py

View File

@ -2,36 +2,27 @@
Glued together by [Łukasz Langa](mailto:lukasz@langa.pl). Glued together by [Łukasz Langa](mailto:lukasz@langa.pl).
Maintained with: Maintained with [Carol Willing](mailto:carolcode@willingconsulting.com),
[Carl Meyer](mailto:carl@oddbird.net),
- [Carol Willing](mailto:carolcode@willingconsulting.com) [Jelle Zijlstra](mailto:jelle.zijlstra@gmail.com),
- [Carl Meyer](mailto:carl@oddbird.net) [Mika Naylor](mailto:mail@autophagy.io),
- [Jelle Zijlstra](mailto:jelle.zijlstra@gmail.com) [Zsolt Dollenstein](mailto:zsol.zsol@gmail.com),
- [Mika Naylor](mailto:mail@autophagy.io) [Cooper Lees](mailto:me@cooperlees.com), and Richard Si.
- [Zsolt Dollenstein](mailto:zsol.zsol@gmail.com)
- [Cooper Lees](mailto:me@cooperlees.com)
- [Richard Si](mailto:sichard26@gmail.com)
- [Felix Hildén](mailto:felix.hilden@gmail.com)
- [Batuhan Taskaya](mailto:batuhan@python.org)
- [Shantanu Jain](mailto:hauntsaninja@gmail.com)
Multiple contributions by: Multiple contributions by:
- [Abdur-Rahmaan Janhangeer](mailto:arj.python@gmail.com) - [Abdur-Rahmaan Janhangeer](mailto:arj.python@gmail.com)
- [Adam Johnson](mailto:me@adamj.eu) - [Adam Johnson](mailto:me@adamj.eu)
- [Adam Williamson](mailto:adamw@happyassassin.net) - [Adam Williamson](mailto:adamw@happyassassin.net)
- [Alexander Huynh](mailto:ahrex-gh-psf-black@e.sc) - [Alexander Huynh](mailto:github@grande.coffee)
- [Alexandr Artemyev](mailto:mogost@gmail.com)
- [Alex Vandiver](mailto:github@chmrr.net) - [Alex Vandiver](mailto:github@chmrr.net)
- [Allan Simon](mailto:allan.simon@supinfo.com) - [Allan Simon](mailto:allan.simon@supinfo.com)
- Anders-Petter Ljungquist - Anders-Petter Ljungquist
- [Amethyst Reese](mailto:amy@n7.gg)
- [Andrew Thorp](mailto:andrew.thorp.dev@gmail.com) - [Andrew Thorp](mailto:andrew.thorp.dev@gmail.com)
- [Andrew Zhou](mailto:andrewfzhou@gmail.com) - [Andrew Zhou](mailto:andrewfzhou@gmail.com)
- [Andrey](mailto:dyuuus@yandex.ru) - [Andrey](mailto:dyuuus@yandex.ru)
- [Andy Freeland](mailto:andy@andyfreeland.net) - [Andy Freeland](mailto:andy@andyfreeland.net)
- [Anthony Sottile](mailto:asottile@umich.edu) - [Anthony Sottile](mailto:asottile@umich.edu)
- [Antonio Ossa Guerra](mailto:aaossa+black@uc.cl)
- [Arjaan Buijk](mailto:arjaan.buijk@gmail.com) - [Arjaan Buijk](mailto:arjaan.buijk@gmail.com)
- [Arnav Borbornah](mailto:arnavborborah11@gmail.com) - [Arnav Borbornah](mailto:arnavborborah11@gmail.com)
- [Artem Malyshev](mailto:proofit404@gmail.com) - [Artem Malyshev](mailto:proofit404@gmail.com)
@ -82,7 +73,6 @@ Multiple contributions by:
- [Hugo Barrera](mailto::hugo@barrera.io) - [Hugo Barrera](mailto::hugo@barrera.io)
- Hugo van Kemenade - Hugo van Kemenade
- [Hynek Schlawack](mailto:hs@ox.cx) - [Hynek Schlawack](mailto:hs@ox.cx)
- [Ionite](mailto:dev@ionite.io)
- [Ivan Katanić](mailto:ivan.katanic@gmail.com) - [Ivan Katanić](mailto:ivan.katanic@gmail.com)
- [Jakub Kadlubiec](mailto:jakub.kadlubiec@skyscanner.net) - [Jakub Kadlubiec](mailto:jakub.kadlubiec@skyscanner.net)
- [Jakub Warczarek](mailto:jakub.warczarek@gmail.com) - [Jakub Warczarek](mailto:jakub.warczarek@gmail.com)
@ -153,7 +143,6 @@ Multiple contributions by:
- [Rishikesh Jha](mailto:rishijha424@gmail.com) - [Rishikesh Jha](mailto:rishijha424@gmail.com)
- [Rupert Bedford](mailto:rupert@rupertb.com) - [Rupert Bedford](mailto:rupert@rupertb.com)
- Russell Davis - Russell Davis
- [Sagi Shadur](mailto:saroad2@gmail.com)
- [Rémi Verschelde](mailto:rverschelde@gmail.com) - [Rémi Verschelde](mailto:rverschelde@gmail.com)
- [Sami Salonen](mailto:sakki@iki.fi) - [Sami Salonen](mailto:sakki@iki.fi)
- [Samuel Cormier-Iijima](mailto:samuel@cormier-iijima.com) - [Samuel Cormier-Iijima](mailto:samuel@cormier-iijima.com)
@ -181,7 +170,6 @@ Multiple contributions by:
- [Tony Narlock](mailto:tony@git-pull.com) - [Tony Narlock](mailto:tony@git-pull.com)
- [Tsuyoshi Hombashi](mailto:tsuyoshi.hombashi@gmail.com) - [Tsuyoshi Hombashi](mailto:tsuyoshi.hombashi@gmail.com)
- [Tushar Chandra](mailto:tusharchandra2018@u.northwestern.edu) - [Tushar Chandra](mailto:tusharchandra2018@u.northwestern.edu)
- [Tushar Sadhwani](mailto:tushar.sadhwani000@gmail.com)
- [Tzu-ping Chung](mailto:uranusjr@gmail.com) - [Tzu-ping Chung](mailto:uranusjr@gmail.com)
- [Utsav Shah](mailto:ukshah2@illinois.edu) - [Utsav Shah](mailto:ukshah2@illinois.edu)
- utsav-dbx - utsav-dbx

1262
CHANGES.md

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
cff-version: 1.2.0
title: "Black: The uncompromising Python code formatter"
message: >-
If you use this software, please cite it using the metadata from this file.
type: software
authors:
- family-names: Langa
given-names: Łukasz
- name: "contributors to Black"
repository-code: "https://github.com/psf/black"
url: "https://black.readthedocs.io/en/stable/"
abstract: >-
Black is the uncompromising Python code formatter. By using it, you agree to cede
control over minutiae of hand-formatting. In return, Black gives you speed,
determinism, and freedom from pycodestyle nagging about formatting. You will save time
and mental energy for more important matters.
Blackened code looks the same regardless of the project you're reading. Formatting
becomes transparent after a while and you can focus on the content instead.
Black makes code review faster by producing the smallest diffs possible.
license: MIT

View File

@ -1,13 +1,10 @@
# Contributing to _Black_ # Contributing to _Black_
Welcome future contributor! We're happy to see you willing to make the project better. Welcome! Happy to see you willing to make the project better. Have you read the entire
[user documentation](https://black.readthedocs.io/en/latest/) yet?
If you aren't familiar with _Black_, or are looking for documentation on something Our [contributing documentation](https://black.readthedocs.org/en/latest/contributing/)
specific, the [user documentation](https://black.readthedocs.io/en/latest/) is the best contains details on all you need to know about contributing to _Black_, the basics to
place to look. the internals of _Black_.
For getting started on contributing, please read the We look forward to your contributions!
[contributing documentation](https://black.readthedocs.org/en/latest/contributing/) for
all you need to know.
Thank you, and we look forward to your contributions!

View File

@ -1,22 +1,14 @@
FROM python:3.12-slim AS builder FROM python:3-slim
RUN mkdir /src RUN mkdir /src
COPY . /src/ COPY . /src/
ENV VIRTUAL_ENV=/opt/venv RUN pip install --no-cache-dir --upgrade pip setuptools wheel \
ENV HATCH_BUILD_HOOKS_ENABLE=1 && apt update && apt install -y git \
# Install build tools to compile black + dependencies && cd /src \
RUN apt update && apt install -y build-essential git python3-dev && pip install --no-cache-dir .[colorama,d] \
RUN python -m venv $VIRTUAL_ENV && rm -rf /src \
RUN python -m pip install --no-cache-dir hatch hatch-fancy-pypi-readme hatch-vcs && apt remove -y git \
RUN . /opt/venv/bin/activate && pip install --no-cache-dir --upgrade pip setuptools \ && apt autoremove -y \
&& cd /src && hatch build -t wheel \ && rm -rf /var/lib/apt/lists/*
&& pip install --no-cache-dir dist/*-cp* \
&& pip install black[colorama,d,uvloop]
FROM python:3.12-slim CMD ["black"]
# copy only Python packages to limit the image size
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
CMD ["/opt/venv/bin/black"]

1
MANIFEST.in Normal file
View File

@ -0,0 +1 @@
prune profiling

54
Pipfile Normal file
View File

@ -0,0 +1,54 @@
[[source]]
name = "pypi"
url = "https://pypi.python.org/simple"
verify_ssl = true
[dev-packages]
# Testing related requirements.
coverage = ">= 5.3"
pytest = " >= 6.1.1"
pytest-mock = ">= 3.3.1"
pytest-cases = ">= 2.3.0"
pytest-xdist = ">= 2.2.1"
pytest-cov = ">= 2.11.1"
parameterized = ">= 0.7.4"
tox = "*"
# Linting related requirements.
pre-commit = ">=2.9.2"
flake8 = ">=3.9.2"
flake8-bugbear = "*"
mypy = ">=0.910"
types-dataclasses = ">=0.1.3"
types-typed-ast = ">=1.4.1"
types-PyYAML = ">=5.4.1"
# Documentation related requirements.
Sphinx = ">=4.1.2"
MyST-Parser = ">=0.15.1"
sphinxcontrib-programoutput = ">=0.17"
sphinx-copybutton = ">=0.4.0"
docutils = "==0.17.1" # not a direct dependency, see https://github.com/pypa/pipenv/issues/3865
# Packaging related requirements.
setuptools = ">=39.2.0"
setuptools-scm = "*"
twine = ">=1.11.0"
wheel = ">=0.31.1"
readme_renderer = "*"
black = {editable = true, extras = ["d", "jupyter"], path = "."}
[packages]
aiohttp = ">=3.6.0"
aiohttp-cors = ">=0.4.0"
platformdirs= ">=2"
click = ">=8.0.0"
mypy_extensions = ">=0.4.3"
pathspec = ">=0.8.1"
regex = ">=2020.1.8"
tomli = ">=0.2.6, <2.0.0"
typed-ast = "==1.4.2"
typing_extensions = {"python_version <" = "3.10","version >=" = "3.10.0.0"}
black = {editable = true,extras = ["d"],path = "."}
dataclasses = {"python_version <" = "3.7","version >" = "0.1.3"}

1649
Pipfile.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,15 @@
[![Black Logo](https://raw.githubusercontent.com/psf/black/main/docs/_static/logo2-readme.png)](https://black.readthedocs.io/en/stable/) ![Black Logo](https://raw.githubusercontent.com/psf/black/main/docs/_static/logo2-readme.png)
<h2 align="center">The Uncompromising Code Formatter</h2> <h2 align="center">The Uncompromising Code Formatter</h2>
<p align="center"> <p align="center">
<a href="https://github.com/psf/black/actions"><img alt="Actions Status" src="https://github.com/psf/black/workflows/Test/badge.svg"></a> <a href="https://github.com/psf/black/actions"><img alt="Actions Status" src="https://github.com/psf/black/workflows/Test/badge.svg"></a>
<a href="https://github.com/psf/black/actions"><img alt="Actions Status" src="https://github.com/psf/black/workflows/Primer/badge.svg"></a>
<a href="https://black.readthedocs.io/en/stable/?badge=stable"><img alt="Documentation Status" src="https://readthedocs.org/projects/black/badge/?version=stable"></a> <a href="https://black.readthedocs.io/en/stable/?badge=stable"><img alt="Documentation Status" src="https://readthedocs.org/projects/black/badge/?version=stable"></a>
<a href="https://coveralls.io/github/psf/black?branch=main"><img alt="Coverage Status" src="https://coveralls.io/repos/github/psf/black/badge.svg?branch=main"></a> <a href="https://coveralls.io/github/psf/black?branch=main"><img alt="Coverage Status" src="https://coveralls.io/repos/github/psf/black/badge.svg?branch=main"></a>
<a href="https://github.com/psf/black/blob/main/LICENSE"><img alt="License: MIT" src="https://black.readthedocs.io/en/stable/_static/license.svg"></a> <a href="https://github.com/psf/black/blob/main/LICENSE"><img alt="License: MIT" src="https://black.readthedocs.io/en/stable/_static/license.svg"></a>
<a href="https://pypi.org/project/black/"><img alt="PyPI" src="https://img.shields.io/pypi/v/black"></a> <a href="https://pypi.org/project/black/"><img alt="PyPI" src="https://img.shields.io/pypi/v/black"></a>
<a href="https://pepy.tech/project/black"><img alt="Downloads" src="https://static.pepy.tech/badge/black"></a> <a href="https://pepy.tech/project/black"><img alt="Downloads" src="https://pepy.tech/badge/black"></a>
<a href="https://anaconda.org/conda-forge/black/"><img alt="conda-forge" src="https://img.shields.io/conda/dn/conda-forge/black.svg?label=conda-forge"></a> <a href="https://anaconda.org/conda-forge/black/"><img alt="conda-forge" src="https://img.shields.io/conda/dn/conda-forge/black.svg?label=conda-forge"></a>
<a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a> <a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
</p> </p>
@ -38,12 +39,14 @@ Try it out now using the [Black Playground](https://black.vercel.app). Watch the
### Installation ### Installation
_Black_ can be installed by running `pip install black`. It requires Python 3.9+ to run. _Black_ can be installed by running `pip install black`. It requires Python 3.6.2+ to
If you want to format Jupyter Notebooks, install with `pip install "black[jupyter]"`. run. If you want to format Python 2 code as well, install with
`pip install black[python2]`. If you want to format Jupyter Notebooks, install with
`pip install black[jupyter]`.
If you can't wait for the latest _hotness_ and want to install from GitHub, use: If you can't wait for the latest _hotness_ and want to install from GitHub, use:
`pip install git+https://github.com/psf/black` `pip install git+git://github.com/psf/black`
### Usage ### Usage
@ -63,13 +66,16 @@ Further information can be found in our docs:
- [Usage and Configuration](https://black.readthedocs.io/en/stable/usage_and_configuration/index.html) - [Usage and Configuration](https://black.readthedocs.io/en/stable/usage_and_configuration/index.html)
### NOTE: This is a beta product
_Black_ is already [successfully used](https://github.com/psf/black#used-by) by many _Black_ is already [successfully used](https://github.com/psf/black#used-by) by many
projects, small and big. _Black_ has a comprehensive test suite, with efficient parallel projects, small and big. Black has a comprehensive test suite, with efficient parallel
tests, and our own auto formatting and parallel Continuous Integration runner. Now that tests, and our own auto formatting and parallel Continuous Integration runner. However,
we have become stable, you should not expect large formatting changes in the future. _Black_ is still beta. Things will probably be wonky for a while. This is made explicit
Stylistic changes will mostly be responses to bug reports and support for new Python by the "Beta" trove classifier, as well as by the "b" in the version number. What this
syntax. For more information please refer to means for you is that **until the formatter becomes stable, you should expect some
[The Black Code Style](https://black.readthedocs.io/en/stable/the_black_code_style/index.html). formatting to change in the future**. That being said, no drastic stylistic changes are
planned, mostly responses to bug reports.
Also, as a safety measure which slows down processing, _Black_ will check that the Also, as a safety measure which slows down processing, _Black_ will check that the
reformatted code still produces a valid AST that is effectively equivalent to the reformatted code still produces a valid AST that is effectively equivalent to the
@ -81,20 +87,14 @@ section for details). If you're feeling confident, use `--fast`.
_Black_ is a PEP 8 compliant opinionated formatter. _Black_ reformats entire files in _Black_ is a PEP 8 compliant opinionated formatter. _Black_ reformats entire files in
place. Style configuration options are deliberately limited and rarely added. It doesn't place. Style configuration options are deliberately limited and rarely added. It doesn't
take previous formatting into account (see take previous formatting into account (see [Pragmatism](#pragmatism) for exceptions).
[Pragmatism](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#pragmatism)
for exceptions).
Our documentation covers the current _Black_ code style, but planned changes to it are Our documentation covers the current _Black_ code style, but planned changes to it are
also documented. They're both worth taking a look at: also documented. They're both worth taking a look:
- [The _Black_ Code Style: Current style](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html) - [The _Black_ Code Style: Current style](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html)
- [The _Black_ Code Style: Future style](https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html) - [The _Black_ Code Style: Future style](https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html)
Changes to the _Black_ code style are bound by the Stability Policy:
- [The _Black_ Code Style: Stability Policy](https://black.readthedocs.io/en/stable/the_black_code_style/index.html#stability-policy)
Please refer to this document before submitting an issue. What seems like a bug might be Please refer to this document before submitting an issue. What seems like a bug might be
intended behaviour. intended behaviour.
@ -132,13 +132,12 @@ code in compliance with many other _Black_ formatted projects.
## Used by ## Used by
The following notable open-source projects trust _Black_ with enforcing a consistent The following notable open-source projects trust _Black_ with enforcing a consistent
code style: pytest, tox, Pyramid, Django, Django Channels, Hypothesis, attrs, code style: pytest, tox, Pyramid, Django Channels, Hypothesis, attrs, SQLAlchemy,
SQLAlchemy, Poetry, PyPA applications (Warehouse, Bandersnatch, Pipenv, virtualenv), Poetry, PyPA applications (Warehouse, Bandersnatch, Pipenv, virtualenv), pandas, Pillow,
pandas, Pillow, Twisted, LocalStack, every Datadog Agent Integration, Home Assistant, Twisted, LocalStack, every Datadog Agent Integration, Home Assistant, Zulip, and many
Zulip, Kedro, OpenOA, FLORIS, ORBIT, WOMBAT, and many more. more.
The following organizations use _Black_: Dropbox, KeepTruckin, Lyft, Mozilla, Quora, The following organizations use _Black_: Facebook, Dropbox, Mozilla, Quora, Duolingo.
Duolingo, QuantumBlack, Tesla, Archer Aviation.
Are we missing anyone? Let us know. Are we missing anyone? Let us know.
@ -165,8 +164,8 @@ Twisted and CPython:
> At least the name is good. > At least the name is good.
**Kenneth Reitz**, creator of [`requests`](https://requests.readthedocs.io/en/latest/) **Kenneth Reitz**, creator of [`requests`](http://python-requests.org/) and
and [`pipenv`](https://readthedocs.org/projects/pipenv/): [`pipenv`](https://readthedocs.org/projects/pipenv/):
> This vastly improves the formatting of our code. Thanks a ton! > This vastly improves the formatting of our code. Thanks a ton!

View File

@ -1,11 +0,0 @@
# Security Policy
## Supported Versions
Only the latest non-prerelease version is supported.
## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the
fix and disclosure.

View File

@ -5,18 +5,13 @@ inputs:
options: options:
description: description:
"Options passed to Black. Use `black --help` to see available options. Default: "Options passed to Black. Use `black --help` to see available options. Default:
'--check --diff'" '--check'"
required: false required: false
default: "--check --diff" default: "--check --diff"
src: src:
description: "Source to run Black. Default: '.'" description: "Source to run Black. Default: '.'"
required: false required: false
default: "." default: "."
jupyter:
description:
"Set this option to true to include Jupyter Notebook files. Default: false"
required: false
default: false
black_args: black_args:
description: "[DEPRECATED] Black input arguments." description: "[DEPRECATED] Black input arguments."
required: false required: false
@ -27,53 +22,38 @@ inputs:
description: 'Python Version specifier (PEP440) - e.g. "21.5b1"' description: 'Python Version specifier (PEP440) - e.g. "21.5b1"'
required: false required: false
default: "" default: ""
use_pyproject:
description: Read Black version specifier from pyproject.toml if `true`.
required: false
default: "false"
summary:
description: "Whether to add the output to the workflow summary"
required: false
default: true
branding: branding:
color: "black" color: "black"
icon: "check-circle" icon: "check-circle"
runs: runs:
using: composite using: composite
steps: steps:
- name: black - run: |
run: | # Exists since using github.action_path + path to main script doesn't work because bash
# Even when black fails, do not close the shell # interprets the backslashes in github.action_path (which are used when the runner OS
set +e # is Windows) destroying the path to the target file.
#
# Also semicolons are necessary because I can't get the newlines to work
entrypoint="import sys;
import subprocess;
from pathlib import Path;
MAIN_SCRIPT = Path(r'${{ github.action_path }}') / 'action' / 'main.py';
proc = subprocess.run([sys.executable, str(MAIN_SCRIPT)]);
sys.exit(proc.returncode)
"
if [ "$RUNNER_OS" == "Windows" ]; then if [ "$RUNNER_OS" == "Windows" ]; then
runner="python" echo $entrypoint | python
else else
runner="python3" echo $entrypoint | python3
fi fi
out=$(${runner} $GITHUB_ACTION_PATH/action/main.py)
exit_code=$?
# Display the raw output in the step
echo "${out}"
if [ "${{ inputs.summary }}" == "true" ]; then
# Display the Markdown output in the job summary
echo "\`\`\`python" >> $GITHUB_STEP_SUMMARY
echo "${out}" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
fi
# Exit with the exit-code returned by Black
exit ${exit_code}
env: env:
# TODO: Remove once https://github.com/actions/runner/issues/665 is fixed. # TODO: Remove once https://github.com/actions/runner/issues/665 is fixed.
INPUT_OPTIONS: ${{ inputs.options }} INPUT_OPTIONS: ${{ inputs.options }}
INPUT_SRC: ${{ inputs.src }} INPUT_SRC: ${{ inputs.src }}
INPUT_JUPYTER: ${{ inputs.jupyter }}
INPUT_BLACK_ARGS: ${{ inputs.black_args }} INPUT_BLACK_ARGS: ${{ inputs.black_args }}
INPUT_VERSION: ${{ inputs.version }} INPUT_VERSION: ${{ inputs.version }}
INPUT_USE_PYPROJECT: ${{ inputs.use_pyproject }}
pythonioencoding: utf-8 pythonioencoding: utf-8
shell: bash shell: bash

View File

@ -1,182 +1,39 @@
import os import os
import re
import shlex import shlex
import shutil
import sys import sys
from pathlib import Path from pathlib import Path
from subprocess import PIPE, STDOUT, run from subprocess import run, PIPE, STDOUT
from typing import Union
ACTION_PATH = Path(os.environ["GITHUB_ACTION_PATH"]) ACTION_PATH = Path(os.environ["GITHUB_ACTION_PATH"])
ENV_PATH = ACTION_PATH / ".black-env" ENV_PATH = ACTION_PATH / ".black-env"
ENV_BIN = ENV_PATH / ("Scripts" if sys.platform == "win32" else "bin") ENV_BIN = ENV_PATH / ("Scripts" if sys.platform == "win32" else "bin")
OPTIONS = os.getenv("INPUT_OPTIONS", default="") OPTIONS = os.getenv("INPUT_OPTIONS", default="")
SRC = os.getenv("INPUT_SRC", default="") SRC = os.getenv("INPUT_SRC", default="")
JUPYTER = os.getenv("INPUT_JUPYTER") == "true"
BLACK_ARGS = os.getenv("INPUT_BLACK_ARGS", default="") BLACK_ARGS = os.getenv("INPUT_BLACK_ARGS", default="")
VERSION = os.getenv("INPUT_VERSION", default="") VERSION = os.getenv("INPUT_VERSION", default="")
USE_PYPROJECT = os.getenv("INPUT_USE_PYPROJECT") == "true"
BLACK_VERSION_RE = re.compile(r"^black([^A-Z0-9._-]+.*)$", re.IGNORECASE)
EXTRAS_RE = re.compile(r"\[.*\]")
EXPORT_SUBST_FAIL_RE = re.compile(r"\$Format:.*\$")
def determine_version_specifier() -> str:
"""Determine the version of Black to install.
The version can be specified either via the `with.version` input or via the
pyproject.toml file if `with.use_pyproject` is set to `true`.
"""
if USE_PYPROJECT and VERSION:
print(
"::error::'with.version' and 'with.use_pyproject' inputs are "
"mutually exclusive.",
file=sys.stderr,
flush=True,
)
sys.exit(1)
if USE_PYPROJECT:
return read_version_specifier_from_pyproject()
elif VERSION and VERSION[0] in "0123456789":
return f"=={VERSION}"
else:
return VERSION
def read_version_specifier_from_pyproject() -> str:
if sys.version_info < (3, 11):
print(
"::error::'with.use_pyproject' input requires Python 3.11 or later.",
file=sys.stderr,
flush=True,
)
sys.exit(1)
import tomllib # type: ignore[import-not-found,unreachable]
try:
with Path("pyproject.toml").open("rb") as fp:
pyproject = tomllib.load(fp)
except FileNotFoundError:
print(
"::error::'with.use_pyproject' input requires a pyproject.toml file.",
file=sys.stderr,
flush=True,
)
sys.exit(1)
version = pyproject.get("tool", {}).get("black", {}).get("required-version")
if version is not None:
return f"=={version}"
arrays = [
*pyproject.get("dependency-groups", {}).values(),
pyproject.get("project", {}).get("dependencies"),
*pyproject.get("project", {}).get("optional-dependencies", {}).values(),
]
for array in arrays:
version = find_black_version_in_array(array)
if version is not None:
break
if version is None:
print(
"::error::'black' dependency missing from pyproject.toml.",
file=sys.stderr,
flush=True,
)
sys.exit(1)
return version
def find_black_version_in_array(array: object) -> Union[str, None]:
if not isinstance(array, list):
return None
try:
for item in array:
# Rudimentary PEP 508 parsing.
item = item.split(";")[0]
item = EXTRAS_RE.sub("", item).strip()
if item == "black":
print(
"::error::Version specifier missing for 'black' dependency in "
"pyproject.toml.",
file=sys.stderr,
flush=True,
)
sys.exit(1)
elif m := BLACK_VERSION_RE.match(item):
return m.group(1).strip()
except TypeError:
pass
return None
run([sys.executable, "-m", "venv", str(ENV_PATH)], check=True) run([sys.executable, "-m", "venv", str(ENV_PATH)], check=True)
version_specifier = determine_version_specifier() req = "black[colorama,python2]"
if JUPYTER: if VERSION:
extra_deps = "[colorama,jupyter]" req += f"=={VERSION}"
else:
extra_deps = "[colorama]"
if version_specifier:
req = f"black{extra_deps}{version_specifier}"
else:
describe_name = ""
with open(ACTION_PATH / ".git_archival.txt", encoding="utf-8") as fp:
for line in fp:
if line.startswith("describe-name: "):
describe_name = line[len("describe-name: ") :].rstrip()
break
if not describe_name:
print("::error::Failed to detect action version.", file=sys.stderr, flush=True)
sys.exit(1)
# expected format is one of:
# - 23.1.0
# - 23.1.0-51-g448bba7
# - $Format:%(describe:tags=true,match=*[0-9]*)$ (if export-subst fails)
if (
describe_name.count("-") < 2
and EXPORT_SUBST_FAIL_RE.match(describe_name) is None
):
# the action's commit matches a tag exactly, install exact version from PyPI
req = f"black{extra_deps}=={describe_name}"
else:
# the action's commit does not match any tag, install from the local git repo
req = f".{extra_deps}"
print(f"Installing {req}...", flush=True)
pip_proc = run( pip_proc = run(
[str(ENV_BIN / "python"), "-m", "pip", "install", req], [str(ENV_BIN / "python"), "-m", "pip", "install", req],
stdout=PIPE, stdout=PIPE,
stderr=STDOUT, stderr=STDOUT,
encoding="utf-8", encoding="utf-8",
cwd=ACTION_PATH,
) )
if pip_proc.returncode: if pip_proc.returncode:
print(pip_proc.stdout) print(pip_proc.stdout)
print("::error::Failed to install Black.", file=sys.stderr, flush=True) print("::error::Failed to install Black.", flush=True)
sys.exit(pip_proc.returncode) sys.exit(pip_proc.returncode)
base_cmd = [str(ENV_BIN / "black")] base_cmd = [str(ENV_BIN / "black")]
if BLACK_ARGS: if BLACK_ARGS:
# TODO: remove after a while since this is deprecated in favour of SRC + OPTIONS. # TODO: remove after a while since this is deprecated in favour of SRC + OPTIONS.
proc = run( proc = run([*base_cmd, *shlex.split(BLACK_ARGS)])
[*base_cmd, *shlex.split(BLACK_ARGS)],
stdout=PIPE,
stderr=STDOUT,
encoding="utf-8",
)
else: else:
proc = run( proc = run([*base_cmd, *shlex.split(OPTIONS), *shlex.split(SRC)])
[*base_cmd, *shlex.split(OPTIONS), *shlex.split(SRC)],
stdout=PIPE,
stderr=STDOUT,
encoding="utf-8",
)
shutil.rmtree(ENV_PATH, ignore_errors=True)
print(proc.stdout)
sys.exit(proc.returncode) sys.exit(proc.returncode)

View File

@ -3,13 +3,8 @@ import collections
import os import os
import sys import sys
import vim import vim
from distutils.util import strtobool
def strtobool(text):
if text.lower() in ['y', 'yes', 't', 'true', 'on', '1']:
return True
if text.lower() in ['n', 'no', 'f', 'false', 'off', '0']:
return False
raise ValueError(f"{text} is not convertible to boolean")
class Flag(collections.namedtuple("FlagBase", "name, cast")): class Flag(collections.namedtuple("FlagBase", "name, cast")):
@property @property
@ -29,12 +24,10 @@ FLAGS = [
Flag(name="fast", cast=strtobool), Flag(name="fast", cast=strtobool),
Flag(name="skip_string_normalization", cast=strtobool), Flag(name="skip_string_normalization", cast=strtobool),
Flag(name="quiet", cast=strtobool), Flag(name="quiet", cast=strtobool),
Flag(name="skip_magic_trailing_comma", cast=strtobool),
Flag(name="preview", cast=strtobool),
] ]
def _get_python_binary(exec_prefix, pyver): def _get_python_binary(exec_prefix):
try: try:
default = vim.eval("g:pymode_python").strip() default = vim.eval("g:pymode_python").strip()
except vim.error: except vim.error:
@ -43,15 +36,7 @@ def _get_python_binary(exec_prefix, pyver):
return default return default
if sys.platform[:3] == "win": if sys.platform[:3] == "win":
return exec_prefix / 'python.exe' return exec_prefix / 'python.exe'
bin_path = exec_prefix / "bin" return exec_prefix / 'bin' / 'python3'
exec_path = (bin_path / f"python{pyver[0]}.{pyver[1]}").resolve()
if exec_path.exists():
return exec_path
# It is possible that some environments may only have python3
exec_path = (bin_path / f"python3").resolve()
if exec_path.exists():
return exec_path
raise ValueError("python executable not found")
def _get_pip(venv_path): def _get_pip(venv_path):
if sys.platform[:3] == "win": if sys.platform[:3] == "win":
@ -64,19 +49,9 @@ def _get_virtualenv_site_packages(venv_path, pyver):
return venv_path / 'lib' / f'python{pyver[0]}.{pyver[1]}' / 'site-packages' return venv_path / 'lib' / f'python{pyver[0]}.{pyver[1]}' / 'site-packages'
def _initialize_black_env(upgrade=False): def _initialize_black_env(upgrade=False):
if vim.eval("g:black_use_virtualenv ? 'true' : 'false'") == "false":
if upgrade:
print("Upgrade disabled due to g:black_use_virtualenv being disabled.")
print("Either use your system package manager (or pip) to upgrade black separately,")
print("or modify your vimrc to have 'let g:black_use_virtualenv = 1'.")
return False
else:
# Nothing needed to be done.
return True
pyver = sys.version_info[:3] pyver = sys.version_info[:3]
if pyver < (3, 9): if pyver < (3, 6, 2):
print("Sorry, Black requires Python 3.9+ to run.") print("Sorry, Black requires Python 3.6.2+ to run.")
return False return False
from pathlib import Path from pathlib import Path
@ -90,7 +65,7 @@ def _initialize_black_env(upgrade=False):
_executable = sys.executable _executable = sys.executable
_base_executable = getattr(sys, "_base_executable", _executable) _base_executable = getattr(sys, "_base_executable", _executable)
try: try:
executable = str(_get_python_binary(Path(sys.exec_prefix), pyver)) executable = str(_get_python_binary(Path(sys.exec_prefix)))
sys.executable = executable sys.executable = executable
sys._base_executable = executable sys._base_executable = executable
print(f'Creating a virtualenv in {virtualenv_path}...') print(f'Creating a virtualenv in {virtualenv_path}...')
@ -123,49 +98,13 @@ if _initialize_black_env():
import black import black
import time import time
def get_target_version(tv): def Black():
if isinstance(tv, black.TargetVersion):
return tv
ret = None
try:
ret = black.TargetVersion[tv.upper()]
except KeyError:
print(f"WARNING: Target version {tv!r} not recognized by Black, using default target")
return ret
def Black(**kwargs):
"""
kwargs allows you to override ``target_versions`` argument of
``black.FileMode``.
``target_version`` needs to be cleaned because ``black.FileMode``
expects the ``target_versions`` argument to be a set of TargetVersion enums.
Allow kwargs["target_version"] to be a string to allow
to type it more quickly.
Using also target_version instead of target_versions to remain
consistent to Black's documentation of the structure of pyproject.toml.
"""
start = time.time() start = time.time()
configs = get_configs() configs = get_configs()
black_kwargs = {}
if "target_version" in kwargs:
target_version = kwargs["target_version"]
if not isinstance(target_version, (list, set)):
target_version = [target_version]
target_version = set(filter(lambda x: x, map(lambda tv: get_target_version(tv), target_version)))
black_kwargs["target_versions"] = target_version
mode = black.FileMode( mode = black.FileMode(
line_length=configs["line_length"], line_length=configs["line_length"],
string_normalization=not configs["skip_string_normalization"], string_normalization=not configs["skip_string_normalization"],
is_pyi=vim.current.buffer.name.endswith('.pyi'), is_pyi=vim.current.buffer.name.endswith('.pyi'),
magic_trailing_comma=not configs["skip_magic_trailing_comma"],
preview=configs["preview"],
**black_kwargs,
) )
quiet = configs["quiet"] quiet = configs["quiet"]
@ -178,9 +117,9 @@ def Black(**kwargs):
) )
except black.NothingChanged: except black.NothingChanged:
if not quiet: if not quiet:
print(f'Black: already well formatted, good job. (took {time.time() - start:.4f}s)') print(f'Already well formatted, good job. (took {time.time() - start:.4f}s)')
except Exception as exc: except Exception as exc:
print(f'Black: {exc}') print(exc)
else: else:
current_buffer = vim.current.window.buffer current_buffer = vim.current.window.buffer
cursors = [] cursors = []
@ -197,7 +136,7 @@ def Black(**kwargs):
except vim.error: except vim.error:
window.cursor = (len(window.buffer), 0) window.cursor = (len(window.buffer), 0)
if not quiet: if not quiet:
print(f'Black: reformatted in {time.time() - start:.4f}s.') print(f'Reformatted in {time.time() - start:.4f}s.')
def get_configs(): def get_configs():
filename = vim.eval("@%") filename = vim.eval("@%")
@ -221,17 +160,8 @@ def BlackVersion():
EndPython3 EndPython3
function black#Black(...) function black#Black()
let kwargs = {} :py3 Black()
for arg in a:000
let arg_list = split(arg, '=')
let kwargs[arg_list[0]] = arg_list[1]
endfor
python3 << EOF
import vim
kwargs = vim.eval("kwargs")
EOF
:py3 Black(**kwargs)
endfunction endfunction
function black#BlackUpgrade() function black#BlackUpgrade()

44
docs/_static/custom.css vendored Normal file
View File

@ -0,0 +1,44 @@
/* Make the sidebar scrollable. Fixes https://github.com/psf/black/issues/990 */
div.sphinxsidebar {
max-height: calc(100% - 18px);
overflow-y: auto;
}
/* Hide scrollbar for Chrome, Safari and Opera */
div.sphinxsidebar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE 6, 7 and 8 */
@media \0screen\, screen\9 {
div.sphinxsidebar {
-ms-overflow-style: none;
}
}
/* Hide scrollbar for IE 9 and 10 */
/* backslash-9 removes ie11+ & old Safari 4 */
@media screen and (min-width: 0\0) {
div.sphinxsidebar {
-ms-overflow-style: none\9;
}
}
/* Hide scrollbar for IE 11 and up */
_:-ms-fullscreen,
:root div.sphinxsidebar {
-ms-overflow-style: none;
}
/* Hide scrollbar for Edge */
@supports (-ms-ime-align: auto) {
div.sphinxsidebar {
-ms-overflow-style: none;
}
}
/* Nicer style for local document toc */
.contents.topic {
background: none;
border: none;
}

View File

@ -1,3 +1,3 @@
[flake8] [flake8]
max-line-length = 88 max-line-length = 88
extend-ignore = E203,E701 extend-ignore = E203

View File

@ -1,3 +1,3 @@
[flake8] [flake8]
max-line-length = 88 max-line-length = 88
extend-ignore = E203,E701 extend-ignore = E203

View File

@ -1,3 +1,3 @@
[flake8] [flake8]
max-line-length = 88 max-line-length = 88
extend-ignore = E203,E701 extend-ignore = E203

View File

@ -1,3 +0,0 @@
[pycodestyle]
max-line-length = 88
ignore = E203,E701

View File

@ -1,3 +0,0 @@
[pycodestyle]
max-line-length = 88
ignore = E203,E701

View File

@ -1,3 +0,0 @@
[pycodestyle]
max-line-length = 88
ignore = E203,E701

View File

@ -1,2 +1,5 @@
[MESSAGES CONTROL]
disable = C0330, C0326
[format] [format]
max-line-length = 88 max-line-length = 88

View File

@ -1,2 +1,5 @@
[tool.pylint.messages_control]
disable = "C0330, C0326"
[tool.pylint.format] [tool.pylint.format]
max-line-length = "88" max-line-length = "88"

View File

@ -1,2 +1,5 @@
[pylint] [pylint]
max-line-length = 88 max-line-length = 88
[pylint.messages_control]
disable = C0330, C0326

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# #
# Configuration file for the Sphinx documentation builder. # Configuration file for the Sphinx documentation builder.
# #
@ -13,12 +14,10 @@
# #
import os import os
import re
import string import string
from importlib.metadata import version
from pathlib import Path from pathlib import Path
from sphinx.application import Sphinx from pkg_resources import get_distribution
CURRENT_DIR = Path(__file__).parent CURRENT_DIR = Path(__file__).parent
@ -26,33 +25,12 @@
def make_pypi_svg(version: str) -> None: def make_pypi_svg(version: str) -> None:
template: Path = CURRENT_DIR / "_static" / "pypi_template.svg" template: Path = CURRENT_DIR / "_static" / "pypi_template.svg"
target: Path = CURRENT_DIR / "_static" / "pypi.svg" target: Path = CURRENT_DIR / "_static" / "pypi.svg"
with open(str(template), encoding="utf8") as f: with open(str(template), "r", encoding="utf8") as f:
svg: str = string.Template(f.read()).substitute(version=version) svg: str = string.Template(f.read()).substitute(version=version)
with open(str(target), "w", encoding="utf8") as f: with open(str(target), "w", encoding="utf8") as f:
f.write(svg) f.write(svg)
def replace_pr_numbers_with_links(content: str) -> str:
"""Replaces all PR numbers with the corresponding GitHub link."""
return re.sub(r"#(\d+)", r"[#\1](https://github.com/psf/black/pull/\1)", content)
def handle_include_read(
app: Sphinx,
relative_path: Path,
parent_docname: str,
content: list[str],
) -> None:
"""Handler for the include-read sphinx event."""
if parent_docname == "change_log":
content[0] = replace_pr_numbers_with_links(content[0])
def setup(app: Sphinx) -> None:
"""Sets up a minimal sphinx extension."""
app.connect("include-read", handle_include_read)
# Necessary so Click doesn't hit an encode error when called by # Necessary so Click doesn't hit an encode error when called by
# sphinxcontrib-programoutput on Windows. # sphinxcontrib-programoutput on Windows.
os.putenv("pythonioencoding", "utf-8") os.putenv("pythonioencoding", "utf-8")
@ -65,7 +43,7 @@ def setup(app: Sphinx) -> None:
# Autopopulate version # Autopopulate version
# The version, including alpha/beta/rc tags, but not commit hash and datestamps # The version, including alpha/beta/rc tags, but not commit hash and datestamps
release = version("black").split("+")[0] release = get_distribution("black").version.split("+")[0]
# The short X.Y version. # The short X.Y version.
version = release version = release
for sp in "abcfr": for sp in "abcfr":
@ -77,7 +55,7 @@ def setup(app: Sphinx) -> None:
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "4.4" needs_sphinx = "3.0"
# Add any Sphinx extension module names here, as strings. They can be # Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@ -109,7 +87,7 @@ def setup(app: Sphinx) -> None:
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # Usually you set "language" from the command line for these cases.
language = "en" language = None
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
@ -127,22 +105,39 @@ def setup(app: Sphinx) -> None:
# Prettier support formatting some MyST syntax but not all, so let's disable the # Prettier support formatting some MyST syntax but not all, so let's disable the
# unsupported yet still enabled by default ones. # unsupported yet still enabled by default ones.
myst_disable_syntax = [ myst_disable_syntax = [
"colon_fence",
"myst_block_break", "myst_block_break",
"myst_line_comment", "myst_line_comment",
"math_block", "math_block",
] ]
# Optional MyST Syntaxes
myst_enable_extensions = []
# -- Options for HTML output ------------------------------------------------- # -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
# #
html_theme = "furo" html_theme = "alabaster"
html_logo = "_static/logo2-readme.png"
html_sidebars = {
"**": [
"about.html",
"navigation.html",
"relations.html",
"searchbox.html",
]
}
html_theme_options = {
"show_related": False,
"description": "“Any color you like.”",
"github_button": True,
"github_user": "psf",
"github_repo": "black",
"github_type": "star",
"show_powered_by": True,
"fixed_sidebar": True,
"logo": "logo2.png",
}
# Add any paths that contain custom static files (such as style sheets) here, # Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files, # relative to this directory. They are copied after the builtin static files,
@ -171,13 +166,15 @@ def setup(app: Sphinx) -> None:
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [( latex_documents = [
(
master_doc, master_doc,
"black.tex", "black.tex",
"Documentation for Black", "Documentation for Black",
"Łukasz Langa and contributors to Black", "Łukasz Langa and contributors to Black",
"manual", "manual",
)] )
]
# -- Options for manual page output ------------------------------------------ # -- Options for manual page output ------------------------------------------
@ -192,7 +189,8 @@ def setup(app: Sphinx) -> None:
# Grouping the document tree into Texinfo files. List of tuples # Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [( texinfo_documents = [
(
master_doc, master_doc,
"Black", "Black",
"Documentation for Black", "Documentation for Black",
@ -200,7 +198,8 @@ def setup(app: Sphinx) -> None:
"Black", "Black",
"The uncompromising Python code formatter", "The uncompromising Python code formatter",
"Miscellaneous", "Miscellaneous",
)] )
]
# -- Options for Epub output ------------------------------------------------- # -- Options for Epub output -------------------------------------------------
@ -228,14 +227,7 @@ def setup(app: Sphinx) -> None:
autodoc_member_order = "bysource" autodoc_member_order = "bysource"
# -- sphinx-copybutton configuration ----------------------------------------
copybutton_prompt_text = (
r">>> |\.\.\. |> |\$ |\# | In \[\d*\]: | {2,5}\.\.\.: | {5,8}: "
)
copybutton_prompt_is_regexp = True
copybutton_remove_prompts = True
# -- Options for intersphinx extension --------------------------------------- # -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library. # Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"<name>": ("https://docs.python.org/3/", None)} intersphinx_mapping = {"https://docs.python.org/3/": None}

View File

@ -7,52 +7,36 @@ It's recommended you evaluate the quantifiable changes your _Black_ formatting
modification causes before submitting a PR. Think about if the change seems disruptive modification causes before submitting a PR. Think about if the change seems disruptive
enough to cause frustration to projects that are already "black formatted". enough to cause frustration to projects that are already "black formatted".
## diff-shades ## black-primer
diff-shades is a tool that runs _Black_ across a list of open-source projects recording `black-primer` is a tool built for CI (and humans) to have _Black_ `--check` a number of
the results. The main highlight feature of diff-shades is being able to compare two Git accessible projects in parallel. (configured in `primer.json`) _(A PR will be
revisions of _Black_. This is incredibly useful as it allows us to see what exact accepted to add Mercurial support.)_
changes will occur, say merging a certain PR.
For more information, please see the [diff-shades documentation][diff-shades]. ### Run flow
### CI integration - 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
diff-shades is also the tool behind the "diff-shades results comparing ..." / ### Speed up runs 🏎
"diff-shades reports zero changes ..." comments on PRs. The project has a GitHub Actions
workflow that analyzes and compares two revisions of _Black_ according to these rules:
| | Baseline revision | Target revision | If you're running locally yourself to test black on lots of code try:
| --------------------- | ----------------------- | ---------------------------- |
| On PRs | latest commit on `main` | PR commit with `main` merged |
| On pushes (main only) | latest PyPI version | the pushed commit |
For pushes to main, there's only one analysis job named `preview-changes` where the - Using `-k` / `--keep` + `-w` / `--work-dir` so you don't have to re-checkout the repo
preview style is used for all projects. each run
For PRs they get one more analysis job: `assert-no-changes`. It's similar to ### CLI arguments
`preview-changes` but runs with the stable code style. It will fail if changes were
made. This makes sure code won't be reformatted again and again within the same year in
accordance to Black's stability policy.
Additionally for PRs, a PR comment will be posted embedding a summary of the preview ```{program-output} black-primer --help
changes and links to further information. If there's a pre-existing diff-shades comment,
it'll be updated instead the next time the workflow is triggered on the same PR.
```{note}
The `preview-changes` job will only fail intentionally if while analyzing a file failed to
format. Otherwise a failure indicates a bug in the workflow.
``` ```
The workflow uploads several artifacts upon completion:
- The raw analyses (.json)
- HTML diffs (.html)
- `.pr-comment.json` (if triggered by a PR)
The last one is downloaded by the `diff-shades-comment` workflow and shouldn't be
downloaded locally. The HTML diffs come in handy for push-based where there's no PR to
post a comment. And the analyses exist just in case you want to do further analysis
using the collected data locally.
[diff-shades]: https://github.com/ichard26/diff-shades#readme

View File

@ -1,45 +0,0 @@
# Contributing
```{toctree}
---
hidden:
---
the_basics
gauging_changes
issue_triage
release_process
```
Welcome! Happy to see you willing to make the project better. Have you read the entire
[user documentation](https://black.readthedocs.io/en/latest/) yet?
```{rubric} Bird's eye view
```
In terms of inspiration, _Black_ is about as configurable as _gofmt_ (which is to say,
not very). This is deliberate. _Black_ aims to provide a consistent style and take away
opportunities for arguing about style.
Bug reports and fixes are always welcome! Please follow the
[issue templates on GitHub](https://github.com/psf/black/issues/new/choose) for best
results.
Before you suggest a new feature or configuration knob, ask yourself why you want it. If
it enables better integration with some workflow, fixes an inconsistency, speeds things
up, and so on - go for it! On the other hand, if your answer is "because I don't like a
particular formatting" then you're not ready to embrace _Black_ yet. Such changes are
unlikely to get accepted. You can still try but prepare to be disappointed.
```{rubric} Contents
```
This section covers the following topics:
- {doc}`the_basics`
- {doc}`gauging_changes`
- {doc}`release_process`
For an overview on contributing to the _Black_, please checkout {doc}`the_basics`.

View File

@ -0,0 +1,43 @@
Contributing
============
.. toctree::
:hidden:
the_basics
gauging_changes
issue_triage
release_process
reference/reference_summary
Welcome! Happy to see you willing to make the project better. Have you read the entire
`user documentation <https://black.readthedocs.io/en/latest/>`_ yet?
.. rubric:: Bird's eye view
In terms of inspiration, *Black* is about as configurable as *gofmt* (which is to say,
not very). This is deliberate. *Black* aims to provide a consistent style and take away
opportunities for arguing about style.
Bug reports and fixes are always welcome! Please follow the
`issue template on GitHub <https://github.com/psf/black/issues/new>`_ for best results.
Before you suggest a new feature or configuration knob, ask yourself why you want it. If
it enables better integration with some workflow, fixes an inconsistency, speeds things
up, and so on - go for it! On the other hand, if your answer is "because I don't like a
particular formatting" then you're not ready to embrace *Black* yet. Such changes are
unlikely to get accepted. You can still try but prepare to be disappointed.
.. rubric:: Contents
This section covers the following topics:
- :doc:`the_basics`
- :doc:`gauging_changes`
- :doc:`release_process`
- :doc:`reference/reference_summary`
For an overview on contributing to the *Black*, please checkout :doc:`the_basics`.
If you need a reference of the functions, classes, etc. available to you while
developing *Black*, there's the :doc:`reference/reference_summary` docs.

View File

@ -1,6 +1,6 @@
# Issue triage # Issue triage
Currently, _Black_ uses the issue tracker for bugs, feature requests, proposed style Currently, _Black_ uses the issue tracker for bugs, feature requests, proposed design
modifications, and general user support. Each of these issues have to be triaged so they modifications, and general user support. Each of these issues have to be triaged so they
can be eventually be resolved somehow. This document outlines the triaging process and can be eventually be resolved somehow. This document outlines the triaging process and
also the current guidelines and recommendations. also the current guidelines and recommendations.
@ -42,7 +42,7 @@ The lifecycle of a bug report or user support issue typically goes something lik
1. _the issue is waiting for triage_ 1. _the issue is waiting for triage_
2. **identified** - has been marked with a type label and other relevant labels, more 2. **identified** - has been marked with a type label and other relevant labels, more
details or a functional reproduction may be still needed (and therefore should be details or a functional reproduction may be still needed (and therefore should be
marked with `S: needs repro` or `S: awaiting response`) marked with `S: needs repro` or `S: awaiting reponse`)
3. **confirmed** - the issue can reproduced and necessary details have been provided 3. **confirmed** - the issue can reproduced and necessary details have been provided
4. **discussion** - initial triage has been done and now the general details on how the 4. **discussion** - initial triage has been done and now the general details on how the
issue should be best resolved are being hashed out issue should be best resolved are being hashed out
@ -53,13 +53,13 @@ The lifecycle of a bug report or user support issue typically goes something lik
- the issue has been fixed - the issue has been fixed
- duplicate of another pre-existing issue or is invalid - duplicate of another pre-existing issue or is invalid
For enhancement, documentation, and style issues, the lifecycle looks very similar but For enhancement, documentation, and design issues, the lifecycle looks very similar but
the details are different: the details are different:
1. _the issue is waiting for triage_ 1. _the issue is waiting for triage_
2. **identified** - has been marked with a type label and other relevant labels 2. **identified** - has been marked with a type label and other relevant labels
3. **discussion** - the merits of the suggested changes are currently being discussed, a 3. **discussion** - the merits of the suggested changes are currently being discussed, a
PR would be acceptable but would be at significant risk of being rejected PR would be acceptable but would be at sigificant risk of being rejected
4. **accepted & awaiting PR** - it's been determined the suggested changes are OK and a 4. **accepted & awaiting PR** - it's been determined the suggested changes are OK and a
PR would be welcomed (`S: accepted`) PR would be welcomed (`S: accepted`)
5. **closed**: - the issue has been resolved, reasons include: 5. **closed**: - the issue has been resolved, reasons include:

View File

@ -0,0 +1,76 @@
*Black* classes
===============
*Contents are subject to change.*
.. currentmodule:: black
:class:`BracketTracker`
-----------------------
.. autoclass:: black.brackets.BracketTracker
:members:
:class:`EmptyLineTracker`
-------------------------
.. autoclass:: black.EmptyLineTracker
:members:
:class:`Line`
-------------
.. autoclass:: black.Line
:members:
:special-members: __str__, __bool__
:class:`LineGenerator`
----------------------
.. autoclass:: black.LineGenerator
:show-inheritance:
:members:
:class:`ProtoComment`
---------------------
.. autoclass:: black.comments.ProtoComment
:members:
:class:`Report`
---------------
.. autoclass:: black.Report
:members:
:special-members: __str__
:class:`Visitor`
----------------
.. autoclass:: black.nodes.Visitor
:show-inheritance:
:members:
Enums
=====
:class:`Changed`
----------------
.. autoclass:: black.Changed
:show-inheritance:
:members:
:class:`Mode`
-----------------
.. autoclass:: black.Mode
:show-inheritance:
:members:
:class:`WriteBack`
------------------
.. autoclass:: black.WriteBack
:show-inheritance:
:members:

View File

@ -0,0 +1,12 @@
*Black* exceptions
==================
*Contents are subject to change.*
.. currentmodule:: black
.. autoexception:: black.linegen.CannotSplit
.. autoexception:: black.NothingChanged
.. autoexception:: black.InvalidInput

View File

@ -0,0 +1,178 @@
*Black* functions
=================
*Contents are subject to change.*
.. currentmodule:: black
Assertions and checks
---------------------
.. autofunction:: black.assert_equivalent
.. autofunction:: black.assert_stable
.. autofunction:: black.lines.can_be_split
.. autofunction:: black.lines.can_omit_invisible_parens
.. autofunction:: black.nodes.is_empty_tuple
.. autofunction:: black.nodes.is_import
.. autofunction:: black.lines.is_line_short_enough
.. autofunction:: black.nodes.is_multiline_string
.. autofunction:: black.nodes.is_one_tuple
.. autofunction:: black.brackets.is_split_after_delimiter
.. autofunction:: black.brackets.is_split_before_delimiter
.. autofunction:: black.nodes.is_stub_body
.. autofunction:: black.nodes.is_stub_suite
.. autofunction:: black.nodes.is_vararg
.. autofunction:: black.nodes.is_yield
Formatting
----------
.. autofunction:: black.format_file_contents
.. autofunction:: black.format_file_in_place
.. autofunction:: black.format_stdin_to_stdout
.. autofunction:: black.format_str
.. autofunction:: black.reformat_one
.. autofunction:: black.schedule_formatting
File operations
---------------
.. autofunction:: black.dump_to_file
.. autofunction:: black.find_project_root
.. autofunction:: black.gen_python_files
.. autofunction:: black.read_pyproject_toml
Parsing
-------
.. autofunction:: black.decode_bytes
.. autofunction:: black.parsing.lib2to3_parse
.. autofunction:: black.parsing.lib2to3_unparse
Split functions
---------------
.. autofunction:: black.linegen.bracket_split_build_line
.. autofunction:: black.linegen.bracket_split_succeeded_or_raise
.. autofunction:: black.linegen.delimiter_split
.. autofunction:: black.linegen.left_hand_split
.. autofunction:: black.linegen.right_hand_split
.. autofunction:: black.linegen.standalone_comment_split
.. autofunction:: black.linegen.transform_line
Caching
-------
.. autofunction:: black.cache.filter_cached
.. autofunction:: black.cache.get_cache_file
.. autofunction:: black.cache.get_cache_info
.. autofunction:: black.cache.read_cache
.. autofunction:: black.cache.write_cache
Utilities
---------
.. py:function:: black.debug.DebugVisitor.show(code: str) -> None
Pretty-print the lib2to3 AST of a given string of `code`.
.. autofunction:: black.concurrency.cancel
.. autofunction:: black.nodes.child_towards
.. autofunction:: black.nodes.container_of
.. autofunction:: black.comments.convert_one_fmt_off_pair
.. autofunction:: black.diff
.. autofunction:: black.linegen.dont_increase_indentation
.. autofunction:: black.numerics.format_float_or_int_string
.. autofunction:: black.nodes.ensure_visible
.. autofunction:: black.lines.enumerate_reversed
.. autofunction:: black.comments.generate_comments
.. autofunction:: black.comments.generate_ignored_nodes
.. autofunction:: black.comments.is_fmt_on
.. autofunction:: black.comments.contains_fmt_on_at_column
.. autofunction:: black.nodes.first_leaf_column
.. autofunction:: black.linegen.generate_trailers_to_omit
.. autofunction:: black.get_future_imports
.. autofunction:: black.comments.list_comments
.. autofunction:: black.comments.make_comment
.. autofunction:: black.linegen.maybe_make_parens_invisible_in_atom
.. autofunction:: black.brackets.max_delimiter_priority_in_atom
.. autofunction:: black.normalize_fmt_off
.. autofunction:: black.numerics.normalize_numeric_literal
.. autofunction:: black.linegen.normalize_prefix
.. autofunction:: black.strings.normalize_string_prefix
.. autofunction:: black.strings.normalize_string_quotes
.. autofunction:: black.linegen.normalize_invisible_parens
.. autofunction:: black.patch_click
.. autofunction:: black.nodes.preceding_leaf
.. autofunction:: black.re_compile_maybe_verbose
.. autofunction:: black.linegen.should_split_line
.. autofunction:: black.shutdown
.. autofunction:: black.strings.sub_twice
.. autofunction:: black.nodes.whitespace

View File

@ -0,0 +1,16 @@
Developer reference
===================
.. note::
The documentation here is quite outdated and has been neglected. Many objects worthy
of inclusion aren't documented. Contributions are appreciated!
*Contents are subject to change.*
.. toctree::
:maxdepth: 2
reference_classes
reference_functions
reference_exceptions

View File

@ -1,174 +1,89 @@
# Release process # Release process
_Black_ has had a lot of work done into standardizing and automating its release _Black_ has had a lot of work automating its release process. This document sets out to
process. This document sets out to explain how everything works and how to release explain what everything does and how to release _Black_ using said automation.
_Black_ using said automation.
## Release cadence ## Cutting a Release
**We aim to release whatever is on `main` every 1-2 months.** This ensures merged To cut a release, you must be a _Black_ maintainer with `GitHub Release` creation
improvements and bugfixes are shipped to users reasonably quickly, while not massively access. Using this access, the release process is:
fracturing the user-base with too many versions. This also keeps the workload on
maintainers consistent and predictable.
If there's not much new on `main` to justify a release, it's acceptable to skip a 1. Cut a new PR editing `CHANGES.md` to version the latest changes
month's release. Ideally January releases should not be skipped because as per our 1. Example PR: https://github.com/psf/black/pull/2192
[stability policy](labels/stability-policy), the first release in a new calendar year 2. Example title: `Update CHANGES.md for XX.X release`
may make changes to the _stable_ style. While the policy applies to the first release 2. Once the release PR is merged ensure all CI passes
(instead of only January releases), confining changes to the stable style to January 1. If not, ensure there is an Issue open for the cause of failing CI (generally we'd
will keep things predictable (and nicer) for users. want this fixed before cutting a release)
3. Open `CHANGES.md` and copy the _raw markdown_ of the latest changes to use in the
description of the GitHub Release.
4. Go and [cut a release](https://github.com/psf/black/releases) using the GitHub UI so
that all workflows noted below are triggered.
1. The release version and tag should be the [CalVer](https://calver.org) version
_Black_ used for the current release e.g. `21.6` / `21.5b1`
2. _Black_ uses [setuptools scm](https://pypi.org/project/setuptools-scm/) to pull
the current version for the package builds and release.
5. Once the release is cut, you're basically done. It's a good practice to go and watch
to make sure all the [GitHub Actions](https://github.com/psf/black/actions) pass,
although you should receive an email to your registered GitHub email address should
one fail.
1. You should see all the release workflows and lint/unittests workflows running on
the new tag in the Actions UI
Unless there is a serious regression or bug that requires immediate patching, **there If anything fails, please go read the respective action's log output and configuration
should not be more than one release per month**. While version numbers are cheap, file to reverse engineer your way to a fix/soluton.
releases require a maintainer to both commit to do the actual cutting of a release, but
also to be able to deal with the potential fallout post-release. Releasing more
frequently than monthly nets rapidly diminishing returns.
## Cutting a release
**You must have `write` permissions for the _Black_ repository to cut a release.**
The 10,000 foot view of the release process is that you prepare a release PR and then
publish a [GitHub Release]. This triggers [release automation](#release-workflows) that
builds all release artifacts and publishes them to the various platforms we publish to.
We now have a `scripts/release.py` script to help with cutting the release PRs.
- `python3 scripts/release.py --help` is your friend.
- `release.py` has only been tested in Python 3.12 (so get with the times :D)
To cut a release:
1. Determine the release's version number
- **_Black_ follows the [CalVer] versioning standard using the `YY.M.N` format**
- So unless there already has been a release during this month, `N` should be `0`
- Example: the first release in January, 2022 → `22.1.0`
- `release.py` will calculate this and log to stderr for you copy paste pleasure
1. File a PR editing `CHANGES.md` and the docs to version the latest changes
- Run `python3 scripts/release.py [--debug]` to generate most changes
- Sub headings in the template, if they have no bullet points need manual removal
_PR welcome to improve :D_
1. If `release.py` fail manually edit; otherwise, yay, skip this step!
1. Replace the `## Unreleased` header with the version number
1. Remove any empty sections for the current release
1. (_optional_) Read through and copy-edit the changelog (eg. by moving entries,
fixing typos, or rephrasing entries)
1. Double-check that no changelog entries since the last release were put in the
wrong section (e.g., run `git diff <last release> CHANGES.md`)
1. Update references to the latest version in
{doc}`/integrations/source_version_control` and
{doc}`/usage_and_configuration/the_basics`
- Example PR: [GH-3139]
1. Once the release PR is merged, wait until all CI passes
- If CI does not pass, **stop** and investigate the failure(s) as generally we'd want
to fix failing CI before cutting a release
1. [Draft a new GitHub Release][new-release]
1. Click `Choose a tag` and type in the version number, then select the
`Create new tag: YY.M.N on publish` option that appears
1. Verify that the new tag targets the `main` branch
1. You can leave the release title blank, GitHub will default to the tag name
1. Copy and paste the _raw changelog Markdown_ for the current release into the
description box
1. Publish the GitHub Release, triggering [release automation](#release-workflows) that
will handle the rest
1. Once CI is done add + commit (git push - No review) a new empty template for the next
release to CHANGES.md _(Template is able to be copy pasted from release.py should we
fail)_
1. `python3 scripts/release.py --add-changes-template|-a [--debug]`
1. Should that fail, please return to copy + paste
1. At this point, you're basically done. It's good practice to go and [watch and verify
that all the release workflows pass][black-actions], although you will receive a
GitHub notification should something fail.
- If something fails, don't panic. Please go read the respective workflow's logs and
configuration file to reverse-engineer your way to a fix/solution.
Congratulations! You've successfully cut a new release of _Black_. Go and stand up and
take a break, you deserve it.
```{important}
Once the release artifacts reach PyPI, you may see new issues being filed indicating
regressions. While regressions are not great, they don't automatically mean a hotfix
release is warranted. Unless the regressions are serious and impact many users, a hotfix
release is probably unnecessary.
In the end, use your best judgement and ask other maintainers for their thoughts.
```
## Release workflows ## Release workflows
All of _Black_'s release automation uses [GitHub Actions]. All workflows are therefore All _Blacks_'s automation workflows use GitHub Actions. All workflows are therefore
configured using YAML files in the `.github/workflows` directory of the _Black_ configured using `.yml` files in the `.github/workflows` directory of the _Black_
repository. repository.
They are triggered by the publication of a [GitHub Release].
Below are descriptions of our release workflows. Below are descriptions of our release workflows.
### Publish to PyPI ### Docker
This is our main workflow. It builds an [sdist] and [wheels] to upload to PyPI where the This workflow uses the QEMU powered `buildx` feature of docker to upload a `arm64` and
vast majority of users will download Black from. It's divided into three job groups: `amd64`/`x86_64` build of the official _Black_ docker image™.
#### sdist + pure wheel - Currently this workflow uses an API Token associated with @cooperlees account
This single job builds the sdist and pure Python wheel (i.e., a wheel that only contains ### pypi_upload
Python code) using [build] and then uploads them to PyPI using [twine]. These artifacts
are general-purpose and can be used on basically any platform supported by Python.
#### mypyc wheels (…) This workflow builds a Python
[sdist](https://docs.python.org/3/distutils/sourcedist.html) and
[wheel](https://pythonwheels.com) using the latest
[setuptools](https://pypi.org/project/setuptools/) and
[wheel](https://pypi.org/project/wheel/) modules.
We use [mypyc] to compile _Black_ into a CPython C extension for significantly improved It will then use [twine](https://pypi.org/project/twine/) to upload both release formats
performance. Wheels built with mypyc are platform and Python version specific. to PyPI for general downloading of the _Black_ Python package. This is where
[Supported platforms are documented in the FAQ](labels/mypyc-support). [pip](https://pypi.org/project/pip/) looks by default.
These matrix jobs use [cibuildwheel] which handles the complicated task of building C - Currently this workflow uses an API token associated with @ambv's PyPI account
extensions for many environments for us. Since building these wheels is slow, there are
multiple mypyc wheels jobs (hence the term "matrix") that build for a specific platform
(as noted in the job name in parentheses).
Like the previous job group, the built wheels are uploaded to PyPI using [twine]. ### Upload self-contained binaries
#### Update stable branch This workflow builds self-contained binaries for multiple platforms. This allows people
to download the executable for their platform and run _Black_ without a
[Python Runtime](https://wiki.python.org/moin/PythonImplementations) installed.
So this job doesn't _really_ belong here, but updating the `stable` branch after the The created binaries are attached/stored on the associated
other PyPI jobs pass (they must pass for this job to start) makes the most sense. This [GitHub Release](https://github.com/psf/black/releases) for download over _IPv4 only_
saves us from remembering to update the branch sometime after cutting the release. (GitHub still does not have IPv6 access 😢).
- _Currently this workflow uses an API token associated with @ambv's PyPI account_ ## Moving the `stable` tag
### Publish executables _Black_ provides a stable tag for people who want to move along as _Black_ developers
deem the newest version reliable. Here the _Black_ developers will move once the release
has been problem free for at least ~24 hours from release. Given the large _Black_
userbase we hear about bad bugs quickly. We do strive to continually improve our CI too.
This workflow builds native executables for multiple platforms using [PyInstaller]. This ### Tag moving process
allows people to download the executable for their platform and run _Black_ without a
[Python runtime](https://wiki.python.org/moin/PythonImplementations) installed.
The created binaries are stored on the associated GitHub Release for download over _IPv4 #### stable
only_ (GitHub still does not have IPv6 access 😢).
### docker From a rebased `main` checkout:
This workflow uses the QEMU powered `buildx` feature of Docker to upload an `arm64` and 1. `git tag -f stable VERSION_TAG`
`amd64`/`x86_64` build of the official _Black_ Docker image™. 1. e.g. `git tag -f stable 21.5b1`
1. `git push --tags -f`
- _Currently this workflow uses an API Token associated with @cooperlees account_
```{note}
This also runs on each push to `main`.
```
[black-actions]: https://github.com/psf/black/actions
[build]: https://pypa-build.readthedocs.io/
[calver]: https://calver.org
[cibuildwheel]: https://cibuildwheel.readthedocs.io/
[gh-3139]: https://github.com/psf/black/pull/3139
[github actions]: https://github.com/features/actions
[github release]: https://github.com/psf/black/releases
[new-release]: https://github.com/psf/black/releases/new
[mypyc]: https://mypyc.readthedocs.io/
[mypyc-platform-support]:
/faq.html#what-is-compiled-yes-no-all-about-in-the-version-output
[pyinstaller]: https://www.pyinstaller.org/
[sdist]:
https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist
[twine]: https://github.com/features/actions
[wheels]: https://packaging.python.org/en/latest/glossary/#term-Wheel

View File

@ -4,27 +4,26 @@ An overview on contributing to the _Black_ project.
## Technicalities ## Technicalities
Development on the latest version of Python is preferred. You can use any operating Development on the latest version of Python is preferred. As of this writing it's 3.9.
system. You can use any operating system.
First clone the _Black_ repository: Install all development dependencies using:
```console ```console
$ git clone https://github.com/psf/black.git $ pipenv install --dev
$ cd black $ pipenv shell
$ pre-commit install
``` ```
Then install development dependencies inside a virtual environment of your choice, for If you haven't used `pipenv` before but are comfortable with virtualenvs, just run
example: `pip install pipenv` in the virtualenv you're already using and invoke the command above
from the cloned _Black_ repo. It will do the correct thing.
Non pipenv install works too:
```console ```console
$ python3 -m venv .venv $ pip install -r test_requirements.txt
$ source .venv/bin/activate # activation for linux and mac $ pip install -e .[d]
$ .venv\Scripts\activate # activation for windows
(.venv)$ pip install -r test_requirements.txt
(.venv)$ pip install -e ".[d]"
(.venv)$ pre-commit install
``` ```
Before submitting pull requests, run lints and tests with the following commands from Before submitting pull requests, run lints and tests with the following commands from
@ -32,125 +31,69 @@ the root of the black repo:
```console ```console
# Linting # Linting
(.venv)$ pre-commit run -a $ pre-commit run -a
# Unit tests # Unit tests
(.venv)$ tox -e py $ tox -e py
# Optional Fuzz testing # Optional Fuzz testing
(.venv)$ tox -e fuzz $ tox -e fuzz
# Format Black itself # Optional CI run to test your changes on many popular python projects
(.venv)$ tox -e run_self $ black-primer [-k -w /tmp/black_test_repos]
``` ```
### Development
Further examples of invoking the tests
```console
# Run all of the above mentioned, in parallel
(.venv)$ tox --parallel=auto
# Run tests on a specific python version
(.venv)$ tox -e py39
# Run an individual test
(.venv)$ pytest -k <test name>
# Pass arguments to pytest
(.venv)$ tox -e py -- --no-cov
# Print full tree diff, see documentation below
(.venv)$ tox -e py -- --print-full-tree
# Disable diff printing, see documentation below
(.venv)$ tox -e py -- --print-tree-diff=False
```
### Testing
All aspects of the _Black_ style should be tested. Normally, tests should be created as
files in the `tests/data/cases` directory. These files consist of up to three parts:
- A line that starts with `# flags: ` followed by a set of command-line options. For
example, if the line is `# flags: --preview --skip-magic-trailing-comma`, the test
case will be run with preview mode on and the magic trailing comma off. The options
accepted are mostly a subset of those of _Black_ itself, except for the
`--minimum-version=` flag, which should be used when testing a grammar feature that
works only in newer versions of Python. This flag ensures that we don't try to
validate the AST on older versions and tests that we autodetect the Python version
correctly when the feature is used. For the exact flags accepted, see the function
`get_flags_parser` in `tests/util.py`. If this line is omitted, the default options
are used.
- A block of Python code used as input for the formatter.
- The line `# output`, followed by the output of _Black_ when run on the previous block.
If this is omitted, the test asserts that _Black_ will leave the input code unchanged.
_Black_ has two pytest command-line options affecting test files in `tests/data/` that
are split into an input part, and an output part, separated by a line with`# output`.
These can be passed to `pytest` through `tox`, or directly into pytest if not using
`tox`.
#### `--print-full-tree`
Upon a failing test, print the full concrete syntax tree (CST) as it is after processing
the input ("actual"), and the tree that's yielded after parsing the output ("expected").
Note that a test can fail with different output with the same CST. This used to be the
default, but now defaults to `False`.
#### `--print-tree-diff`
Upon a failing test, print the diff of the trees as described above. This is the
default. To turn it off pass `--print-tree-diff=False`.
### News / Changelog Requirement ### News / Changelog Requirement
`Black` has CI that will check for an entry corresponding to your PR in `CHANGES.md`. If `Black` has CI that will check for an entry corresponding to your PR in `CHANGES.md`. If
you feel this PR does not require a changelog entry please state that in a comment and a you feel this PR does not require a changelog entry please state that in a comment and a
maintainer can add a `skip news` label to make the CI pass. Otherwise, please ensure you maintainer can add a `skip news` label to make the CI pass. Otherwise, please ensure you
have a line in the following format added below the appropriate header: have a line in the following format:
```md ```md
- `Black` is now more awesome (#X) - `Black` is now more awesome (#X)
``` ```
<!---
The Next PR Number link uses HTML because of a bug in MyST-Parser that double-escapes the ampersand, causing the query parameters to not be processed.
MyST-Parser issue: https://github.com/executablebooks/MyST-Parser/issues/760
MyST-Parser stalled fix PR: https://github.com/executablebooks/MyST-Parser/pull/929
-->
Note that X should be your PR number, not issue number! To workout X, please use Note that X should be your PR number, not issue number! To workout X, please use
<a href="https://ichard26.github.io/next-pr-number/?owner=psf&name=black">Next PR [Next PR Number](https://ichard26.github.io/next-pr-number/?owner=psf&name=black). This
Number</a>. This is not perfect but saves a lot of release overhead as now the releaser is not perfect but saves a lot of release overhead as now the releaser does not need to
does not need to go back and workout what to add to the `CHANGES.md` for each release. go back and workout what to add to the `CHANGES.md` for each release.
### Style Changes ### Style Changes
If a change would affect the advertised code style, please modify the documentation (The If a change would affect the advertised code style, please modify the documentation (The
_Black_ code style) to reflect that change. Patches that fix unintended bugs in _Black_ code style) to reflect that change. Patches that fix unintended bugs in
formatting don't need to be mentioned separately though. If the change is implemented formatting don't need to be mentioned separately though.
with the `--preview` flag, please include the change in the future style document
instead and write the changelog entry under the dedicated "Preview style" heading.
### Docs Testing ### Docs Testing
If you make changes to docs, you can test they still build locally too. If you make changes to docs, you can test they still build locally too.
```console ```console
(.venv)$ pip install -r docs/requirements.txt $ pip install -r docs/requirements.txt
(.venv)$ pip install -e ".[d]" $ pip install [-e] .[d]
(.venv)$ sphinx-build -a -b html -W docs/ docs/_build/ $ sphinx-build -a -b html -W docs/ docs/_build/
``` ```
## black-primer
`black-primer` is used by CI to pull down well-known _Black_ formatted projects and see
if we get source code changes. It will error on formatting changes or errors. Please run
before pushing your PR to see if you get the actions you would expect from _Black_ with
your PR. You may need to change
[primer.json](https://github.com/psf/black/blob/main/src/black_primer/primer.json)
configuration for it to pass.
For more `black-primer` information visit the
[documentation](./gauging_changes.md#black-primer).
## Hygiene ## Hygiene
If you're fixing a bug, add a test. Run it first to confirm it fails, then fix the bug, If you're fixing a bug, add a test. Run it first to confirm it fails, then fix the bug,
and run the test again to confirm it's really fixed. run it again to confirm it's really fixed.
If adding a new feature, add a test. In fact, always add a test. If adding a large If adding a new feature, add a test. In fact, always add a test. But wait, before adding
feature, please first open an issue to discuss it beforehand. any large feature, first open an issue for us to discuss the idea first.
## Finally ## Finally

View File

@ -5,32 +5,20 @@ The most common questions and issues users face are aggregated to this FAQ.
```{contents} ```{contents}
:local: :local:
:backlinks: none :backlinks: none
:class: this-will-duplicate-information-and-it-is-still-useful-here
``` ```
## Why spaces? I prefer tabs
PEP 8 recommends spaces over tabs, and they are used by most of the Python community.
_Black_ provides no options to configure the indentation style, and requests for such
options will not be considered.
However, we recognise that using tabs is an accessibility issue as well. While the
option will never be added to _Black_, visually impaired developers may find conversion
tools such as `expand/unexpand` (for Linux) useful when contributing to Python projects.
A workflow might consist of e.g. setting up appropriate pre-commit and post-merge git
hooks, and scripting `unexpand` to run after applying _Black_.
## Does Black have an API? ## Does Black have an API?
Not yet. _Black_ is fundamentally a command line tool. Many Not yet. _Black_ is fundamentally a command line tool. Many
[integrations](/integrations/index.md) are provided, but a Python interface is not one [integrations](integrations/index.rst) are provided, but a Python interface is not one
of them. A simple API is being [planned](https://github.com/psf/black/issues/779) of them. A simple API is being [planned](https://github.com/psf/black/issues/779)
though. though.
## Is Black safe to use? ## Is Black safe to use?
Yes. _Black_ is strictly about formatting, nothing else. Black strives to ensure that Yes, for the most part. _Black_ is strictly about formatting, nothing else. But because
after formatting the AST is _Black_ is still in [beta](index.rst), some edges are still a bit rough. To combat
issues, the equivalence of code after formatting is
[checked](the_black_code_style/current_style.md#ast-before-and-after-formatting) with [checked](the_black_code_style/current_style.md#ast-before-and-after-formatting) with
limited special cases where the code is allowed to differ. If issues are found, an error limited special cases where the code is allowed to differ. If issues are found, an error
is raised and the file is left untouched. Magical comments that influence linters and is raised and the file is left untouched. Magical comments that influence linters and
@ -38,13 +26,10 @@ other tools, such as `# noqa`, may be moved by _Black_. See below for more detai
## How stable is Black's style? ## How stable is Black's style?
Stable. _Black_ aims to enforce one style and one style only, with some room for Quite stable. _Black_ aims to enforce one style and one style only, with some room for
pragmatism. See [The Black Code Style](the_black_code_style/index.md) for more details. pragmatism. However, _Black_ is still in beta so style changes are both planned and
still proposed on the issue tracker. See
Starting in 2022, the formatting output is stable for the releases made in the same year [The Black Code Style](the_black_code_style/index.rst) for more details.
(other than unintentional bugs). At the beginning of every year, the first release will
make changes to the stable style. It is possible to opt in to the latest formatting
styles using the `--preview` flag.
## Why is my file not formatted? ## Why is my file not formatted?
@ -58,8 +43,6 @@ _Black_ is timid about formatting Jupyter Notebooks. Cells containing any of the
following will not be formatted: following will not be formatted:
- automagics (e.g. `pip install black`) - automagics (e.g. `pip install black`)
- non-Python cell magics (e.g. `%%writefile`). These can be added with the flag
`--python-cell-magics`, e.g. `black --python-cell-magics writefile hello.ipynb`.
- multiline magics, e.g.: - multiline magics, e.g.:
```python ```python
@ -77,26 +60,20 @@ following will not be formatted:
- invalid syntax, as it can't be safely distinguished from automagics in the absence of - invalid syntax, as it can't be safely distinguished from automagics in the absence of
a running `IPython` kernel. a running `IPython` kernel.
## Why does Flake8 report warnings? ## Why are Flake8's E203 and W503 violated?
Some of Flake8's rules conflict with Black's style. We recommend disabling these rules. Because they go against PEP 8. E203 falsely triggers on list
See [Using _Black_ with other tools](labels/why-pycodestyle-warnings). [slices](the_black_code_style/current_style.md#slices), and adhering to W503 hinders
readability because operators are misaligned. Disable W503 and enable the
disabled-by-default counterpart W504. E203 should be disabled while changes are still
[discussed](https://github.com/PyCQA/pycodestyle/issues/373).
## Which Python versions does Black support? ## Does Black support Python 2?
_Black_ generally supports all Python versions supported by CPython (see For formatting, yes! [Install](getting_started.md#installation) with the `python2` extra
[the Python devguide](https://devguide.python.org/versions/) for current information). to format Python 2 files too! There are no current plans to drop support, but most
We promise to support at least all Python versions that have not reached their end of likely it is bound to happen. Sometime. Eventually. In terms of running _Black_ though,
life. This is the case for both running _Black_ and formatting code. Python 3.6 or newer is required.
Support for formatting Python 2 code was removed in version 22.0. While we've made no
plans to stop supporting older Python 3 minor versions immediately, their support might
also be removed some time in the future without a deprecation period.
`await`/`async` as soft keywords/indentifiers are no longer supported as of 25.2.0.
Runtime support for 3.6 was removed in version 22.10.0, for 3.7 in version 23.7.0, and
for 3.8 in version 24.10.0.
## Why does my linter or typechecker complain after I format my code? ## Why does my linter or typechecker complain after I format my code?
@ -105,35 +82,3 @@ influence their behavior. While Black does its best to recognize such comments a
them in the right place, this detection is not and cannot be perfect. Therefore, you'll them in the right place, this detection is not and cannot be perfect. Therefore, you'll
sometimes have to manually move these comments to the right place after you format your sometimes have to manually move these comments to the right place after you format your
codebase with _Black_. codebase with _Black_.
## Can I run Black with PyPy?
Yes, there is support for PyPy 3.8 and higher.
## Why does Black not detect syntax errors in my code?
_Black_ is an autoformatter, not a Python linter or interpreter. Detecting all syntax
errors is not a goal. It can format all code accepted by CPython (if you find an example
where that doesn't hold, please report a bug!), but it may also format some code that
CPython doesn't accept.
(labels/mypyc-support)=
## What is `compiled: yes/no` all about in the version output?
While _Black_ is indeed a pure Python project, we use [mypyc] to compile _Black_ into a
C Python extension, usually doubling performance. These compiled wheels are available
for 64-bit versions of Windows, Linux (via the manylinux standard), and macOS across all
supported CPython versions.
Platforms including musl-based and/or ARM Linux distributions, and ARM Windows are
currently **not** supported. These platforms will fall back to the slower pure Python
wheel available on PyPI.
If you are experiencing exceptionally weird issues or even segfaults, you can try
passing `--no-binary black` to your pip install invocation. This flag excludes all
wheels (including the pure Python wheel), so this command will use the [sdist].
[mypyc]: https://mypyc.readthedocs.io/en/latest/
[sdist]:
https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist

View File

@ -16,14 +16,14 @@ Also, you can try out _Black_ online for minimal fuss on the
## Installation ## Installation
_Black_ can be installed by running `pip install black`. It requires Python 3.9+ to run. _Black_ can be installed by running `pip install black`. It requires Python 3.6.2+ to
If you want to format Jupyter Notebooks, install with `pip install "black[jupyter]"`. run, but can format Python 2 code too. Python 2 support needs the `typed_ast`
dependency, which be installed with `pip install black[python2]`. If you want to format
If you use pipx, you can install Black with `pipx install black`. Jupyter Notebooks, install with `pip install black[jupyter]`.
If you can't wait for the latest _hotness_ and want to install from GitHub, use: If you can't wait for the latest _hotness_ and want to install from GitHub, use:
`pip install git+https://github.com/psf/black` `pip install git+git://github.com/psf/black`
## Basic usage ## Basic usage

View File

@ -1,16 +0,0 @@
# Guides
```{toctree}
---
hidden:
---
introducing_black_to_your_project
using_black_with_other_tools
```
Wondering how to do something specific? You've found the right place! Listed below are
topic specific guides available:
- {doc}`introducing_black_to_your_project`
- {doc}`using_black_with_other_tools`

14
docs/guides/index.rst Normal file
View File

@ -0,0 +1,14 @@
Guides
======
.. toctree::
:hidden:
introducing_black_to_your_project
using_black_with_other_tools
Wondering how to do something specific? You've found the right place! Listed below
are topic specific guides available:
- :doc:`introducing_black_to_your_project`
- :doc:`using_black_with_other_tools`

View File

@ -18,8 +18,7 @@ previous revision that modified those lines.
So when migrating your project's code style to _Black_, reformat everything and commit 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 the changes (preferably in one massive commit). Then put the full 40 characters commit
identifier(s) into a file usually called `.git-blame-ignore-revs` at the root of your identifier(s) into a file.
project directory.
```text ```text
# Migrate code style to Black # Migrate code style to Black
@ -44,9 +43,8 @@ call to `git blame`.
$ git config blame.ignoreRevsFile .git-blame-ignore-revs $ git config blame.ignoreRevsFile .git-blame-ignore-revs
``` ```
**The one caveat is that some online Git-repositories like GitLab do not yet support **The one caveat is that GitHub and GitLab do not yet support ignoring revisions using
ignoring revisions using their native blame UI.** So blame information will be cluttered their native UI of blame.** So blame information will be cluttered with a reformatting
with a reformatting commit on those platforms. (If you'd like this feature, there's an commit on those platforms. (If you'd like this feature, there's an open issue for
open issue for [GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/31423)). [GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/31423) and please let GitHub
[GitHub supports `.git-blame-ignore-revs`](https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view) know!)
by default in blame views however.

View File

@ -51,9 +51,9 @@ line_length = 88
#### Why those options above? #### Why those options above?
_Black_ wraps imports that surpass `line-length` by moving identifiers onto separate _Black_ wraps imports that surpass `line-length` by moving identifiers into their own
lines and by adding a trailing comma after each. A more detailed explanation of this indented line. If that still doesn't fit the bill, it will put all of them in separate
behaviour can be lines and put a trailing comma. A more detailed explanation of this behaviour can be
[found here](../the_black_code_style/current_style.md#how-black-wraps-lines). [found here](../the_black_code_style/current_style.md#how-black-wraps-lines).
isort's default mode of wrapping imports that extend past the `line_length` limit is isort's default mode of wrapping imports that extend past the `line_length` limit is
@ -97,7 +97,7 @@ does not break older versions so you can keep it if you are running previous ver
<details> <details>
<summary>.isort.cfg</summary> <summary>.isort.cfg</summary>
```ini ```cfg
[settings] [settings]
profile = black profile = black
``` ```
@ -107,7 +107,7 @@ profile = black
<details> <details>
<summary>setup.cfg</summary> <summary>setup.cfg</summary>
```ini ```cfg
[isort] [isort]
profile = black profile = black
``` ```
@ -134,10 +134,10 @@ profile = black
</details> </details>
### pycodestyle ### Flake8
[pycodestyle](https://pycodestyle.pycqa.org/) is a code linter. It warns you of syntax [Flake8](https://pypi.org/p/flake8/) is a code linter. It warns you of syntax errors,
errors, possible bugs, stylistic errors, etc. For the most part, pycodestyle follows possible bugs, stylistic errors, etc. For the most part, Flake8 follows
[PEP 8](https://www.python.org/dev/peps/pep-0008/) when warning about stylistic errors. [PEP 8](https://www.python.org/dev/peps/pep-0008/) when warning about stylistic errors.
There are a few deviations that cause incompatibilities with _Black_. There are a few deviations that cause incompatibilities with _Black_.
@ -145,129 +145,96 @@ There are a few deviations that cause incompatibilities with _Black_.
``` ```
max-line-length = 88 max-line-length = 88
ignore = E203,E701 extend-ignore = E203
``` ```
(labels/why-pycodestyle-warnings)=
#### Why those options above? #### Why those options above?
##### `max-line-length`
As with isort, pycodestyle should be configured to allow lines up to the length limit of
`88`, _Black_'s default.
##### `E203`
In some cases, as determined by PEP 8, _Black_ will enforce an equal amount of In some cases, as determined by PEP 8, _Black_ will enforce an equal amount of
whitespace around slice operators. Due to this, pycodestyle will raise whitespace around slice operators. Due to this, Flake8 will raise
`E203 whitespace before ':'` warnings. Since this warning is not PEP 8 compliant, it `E203 whitespace before ':'` warnings. Since this warning is not PEP 8 compliant, Flake8
should be disabled. should be configured to ignore it via `extend-ignore = E203`.
##### `E701` / `E704`
_Black_ will collapse implementations of classes and functions consisting solely of `..`
to a single line. This matches how such examples are formatted in PEP 8. It remains true
that in all other cases Black will prevent multiple statements on the same line, in
accordance with PEP 8 generally discouraging this.
However, `pycodestyle` does not mirror this logic and may raise
`E701 multiple statements on one line (colon)` in this situation. Its
disabled-by-default `E704 multiple statements on one line (def)` rule may also raise
warnings and should not be enabled.
##### `W503`
When breaking a line, _Black_ will break it before a binary operator. This is compliant When breaking a line, _Black_ will break it before a binary operator. This is compliant
with PEP 8 as of with PEP 8 as of
[April 2016](https://github.com/python/peps/commit/c59c4376ad233a62ca4b3a6060c81368bd21e85b#diff-64ec08cc46db7540f18f2af46037f599). [April 2016](https://github.com/python/peps/commit/c59c4376ad233a62ca4b3a6060c81368bd21e85b#diff-64ec08cc46db7540f18f2af46037f599).
There's a disabled-by-default warning in Flake8 which goes against this PEP 8 There's a disabled-by-default warning in Flake8 which goes against this PEP 8
recommendation called `W503 line break before binary operator`. It should not be enabled recommendation called `W503 line break before binary operator`. It should not be enabled
in your configuration. You can use its counterpart in your configuration.
`W504 line break after binary operator` instead.
Also, as like with isort, flake8 should be configured to allow lines up to the length
limit of `88`, _Black_'s default. This explains `max-line-length = 88`.
#### Formats #### Formats
<details> <details>
<summary>setup.cfg, .pycodestyle, tox.ini</summary> <summary>.flake8</summary>
```ini ```ini
[pycodestyle] [flake8]
max-line-length = 88 max-line-length = 88
ignore = E203,E701 extend-ignore = E203
``` ```
</details> </details>
### Flake8 <details>
<summary>setup.cfg</summary>
[Flake8](https://pypi.org/p/flake8/) is a wrapper around multiple linters, including ```cfg
pycodestyle. As such, it has many of the same issues.
#### Bugbear
It's recommended to use [the Bugbear plugin](https://github.com/PyCQA/flake8-bugbear)
and enable
[its B950 check](https://github.com/PyCQA/flake8-bugbear#opinionated-warnings#:~:text=you%20expect%20it.-,B950,-%3A%20Line%20too%20long)
instead of using Flake8's E501, because it aligns with
[Black's 10% rule](labels/line-length).
Install Bugbear and use the following config:
```
[flake8]
max-line-length = 80
extend-select = B950
extend-ignore = E203,E501,E701
```
#### Minimal Configuration
In cases where you can't or don't want to install Bugbear, you can use this minimally
compatible config:
```
[flake8] [flake8]
max-line-length = 88 max-line-length = 88
extend-ignore = E203,E701 extend-ignore = E203
``` ```
#### Why those options above? </details>
See [the pycodestyle section](labels/why-pycodestyle-warnings) above.
#### Formats
<details> <details>
<summary>.flake8, setup.cfg, tox.ini</summary> <summary>tox.ini</summary>
```ini ```ini
[flake8] [flake8]
max-line-length = 88 max-line-length = 88
extend-ignore = E203,E701 extend-ignore = E203
``` ```
</details> </details>
### Pylint ### Pylint
[Pylint](https://pypi.org/p/pylint/) is also a code linter like Flake8. It has many of [Pylint](https://pypi.org/p/pylint/) is also a code linter like Flake8. It has the same
the same checks as Flake8 and more. It particularly has more formatting checks regarding checks as flake8 and more. In particular, it has more formatting checks regarding style
style conventions like variable naming. conventions like variable naming. With so many checks, Pylint is bound to have some
mixed feelings about _Black_'s formatting style.
#### Configuration #### Configuration
``` ```
disable = C0330, C0326
max-line-length = 88 max-line-length = 88
``` ```
#### Why those options above? #### Why those options above?
Pylint should be configured to only complain about lines that surpass `88` characters When _Black_ is folding very long expressions, the closing brackets will
via `max-line-length = 88`. [be dedented](../the_black_code_style/current_style.md#how-black-wraps-lines).
If using `pylint<2.6.0`, also disable `C0326` and `C0330` as these are incompatible with ```py3
_Black_ formatting and have since been removed. ImportantClass.important_method(
exc, limit, lookup_lines, capture_locals, callback
)
```
Although this style is PEP 8 compliant, Pylint will raise
`C0330: Wrong hanging indentation before block (add 4 spaces)` warnings. Since _Black_
isn't configurable on this style, Pylint should be told to ignore these warnings via
`disable = C0330`.
Also, since _Black_ deals with whitespace around operators and brackets, Pylint's
warning `C0326: Bad whitespace` should be disabled using `disable = C0326`.
And as usual, Pylint should be configured to only complain about lines that surpass `88`
characters via `max-line-length = 88`.
#### Formats #### Formats
@ -275,6 +242,9 @@ _Black_ formatting and have since been removed.
<summary>pylintrc</summary> <summary>pylintrc</summary>
```ini ```ini
[MESSAGES CONTROL]
disable = C0330, C0326
[format] [format]
max-line-length = 88 max-line-length = 88
``` ```
@ -287,6 +257,9 @@ max-line-length = 88
```cfg ```cfg
[pylint] [pylint]
max-line-length = 88 max-line-length = 88
[pylint.messages_control]
disable = C0330, C0326
``` ```
</details> </details>
@ -295,6 +268,9 @@ max-line-length = 88
<summary>pyproject.toml</summary> <summary>pyproject.toml</summary>
```toml ```toml
[tool.pylint.messages_control]
disable = "C0330, C0326"
[tool.pylint.format] [tool.pylint.format]
max-line-length = "88" max-line-length = "88"
``` ```

View File

@ -1,139 +0,0 @@
<!--
black documentation master file, created by
sphinx-quickstart on Fri Mar 23 10:53:30 2018.
-->
# The uncompromising code formatter
> “Any color you like.”
By using _Black_, you agree to cede control over minutiae of hand-formatting. In return,
_Black_ gives you speed, determinism, and freedom from `pycodestyle` nagging about
formatting. You will save time and mental energy for more important matters.
_Black_ makes code review faster by producing the smallest diffs possible. Blackened
code looks the same regardless of the project you're reading. Formatting becomes
transparent after a while and you can focus on the content instead.
Try it out now using the [Black Playground](https://black.vercel.app).
```{admonition} Note - Black is now stable!
*Black* is [successfully used](https://github.com/psf/black#used-by) by
many projects, small and big. *Black* has a comprehensive test suite, with efficient
parallel tests, our own auto formatting and parallel Continuous Integration runner.
Now that we have become stable, you should not expect large changes to formatting in
the future. Stylistic changes will mostly be responses to bug reports and support for new Python
syntax.
Also, as a safety measure which slows down processing, *Black* will check that the
reformatted code still produces a valid AST that is effectively equivalent to the
original (see the
[Pragmatism](./the_black_code_style/current_style.md#pragmatism)
section for details). If you're feeling confident, use `--fast`.
```
```{note}
{doc}`Black is licensed under the MIT license <license>`.
```
## Testimonials
**Mike Bayer**, author of [SQLAlchemy](https://www.sqlalchemy.org/):
> _I can't think of any single tool in my entire programming career that has given me a
> bigger productivity increase by its introduction. I can now do refactorings in about
> 1% of the keystrokes that it would have taken me previously when we had no way for
> code to format itself._
**Dusty Phillips**,
[writer](https://smile.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=dusty+phillips):
> _Black is opinionated so you don't have to be._
**Hynek Schlawack**, creator of [attrs](https://www.attrs.org/), core developer of
Twisted and CPython:
> _An auto-formatter that doesn't suck is all I want for Xmas!_
**Carl Meyer**, [Django](https://www.djangoproject.com/) core developer:
> _At least the name is good._
**Kenneth Reitz**, creator of [requests](http://python-requests.org/) and
[pipenv](https://docs.pipenv.org/):
> _This vastly improves the formatting of our code. Thanks a ton!_
## Show your style
Use the badge in your project's README.md:
```md
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
```
Using the badge in README.rst:
```rst
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
```
Looks like this:
```{image} https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
```
## Contents
```{toctree}
---
maxdepth: 3
includehidden:
---
the_black_code_style/index
```
```{toctree}
---
maxdepth: 3
includehidden:
caption: User Guide
---
getting_started
usage_and_configuration/index
integrations/index
guides/index
faq
```
```{toctree}
---
maxdepth: 2
includehidden:
caption: Development
---
contributing/index
change_log
authors
```
```{toctree}
---
hidden:
caption: Project Links
---
GitHub <https://github.com/psf/black>
PyPI <https://pypi.org/project/black>
Chat <https://discord.gg/RtVdv86PrH>
```
# Indices and tables
- {ref}`genindex`
- {ref}`search`

128
docs/index.rst Normal file
View File

@ -0,0 +1,128 @@
.. black documentation master file, created by
sphinx-quickstart on Fri Mar 23 10:53:30 2018.
The uncompromising code formatter
=================================
By using *Black*, you agree to cede control over minutiae of
hand-formatting. In return, *Black* gives you speed, determinism, and
freedom from `pycodestyle` nagging about formatting. You will save time
and mental energy for more important matters.
*Black* makes code review faster by producing the smallest diffs
possible. Blackened code looks the same regardless of the project
you're reading. Formatting becomes transparent after a while and you
can focus on the content instead.
Try it out now using the `Black Playground <https://black.vercel.app>`_.
.. admonition:: Note - this is a beta product
*Black* is already `successfully used <https://github.com/psf/black#used-by>`_ by
many projects, small and big. *Black* has a comprehensive test suite, with efficient
parallel tests, our own auto formatting and parallel Continuous Integration runner.
However, *Black* is still beta. Things will probably be wonky for a while. This is
made explicit by the "Beta" trove classifier, as well as by the "b" in the version
number. What this means for you is that **until the formatter becomes stable, you
should expect some formatting to change in the future**. That being said, no drastic
stylistic changes are planned, mostly responses to bug reports.
Also, as a safety measure which slows down processing, *Black* will check that the
reformatted code still produces a valid AST that is effectively equivalent to the
original (see the
`Pragmatism <./the_black_code_style/current_style.html#pragmatism>`_
section for details). If you're feeling confident, use ``--fast``.
.. note::
:doc:`Black is licensed under the MIT license <license>`.
Testimonials
------------
**Mike Bayer**, author of `SQLAlchemy <https://www.sqlalchemy.org/>`_:
*I can't think of any single tool in my entire programming career that has given me a
bigger productivity increase by its introduction. I can now do refactorings in about
1% of the keystrokes that it would have taken me previously when we had no way for
code to format itself.*
**Dusty Phillips**, `writer <https://smile.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=dusty+phillips>`_:
*Black is opinionated so you don't have to be.*
**Hynek Schlawack**, creator of `attrs <https://www.attrs.org/>`_, core
developer of Twisted and CPython:
*An auto-formatter that doesn't suck is all I want for Xmas!*
**Carl Meyer**, `Django <https://www.djangoproject.com/>`_ core developer:
*At least the name is good.*
**Kenneth Reitz**, creator of `requests <http://python-requests.org/>`_
and `pipenv <https://docs.pipenv.org/>`_:
*This vastly improves the formatting of our code. Thanks a ton!*
Show your style
---------------
Use the badge in your project's README.md:
.. code-block:: md
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
Using the badge in README.rst:
.. code-block:: rst
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
Looks like this:
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
Contents
--------
.. toctree::
:maxdepth: 3
:includehidden:
the_black_code_style/index
.. toctree::
:maxdepth: 3
:includehidden:
getting_started
usage_and_configuration/index
integrations/index
guides/index
faq
.. toctree::
:maxdepth: 3
:includehidden:
contributing/index
change_log
authors
.. toctree::
:hidden:
GitHub ↪ <https://github.com/psf/black>
PyPI ↪ <https://pypi.org/project/black>
Chat ↪ <https://discord.gg/RtVdv86PrH>
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`

View File

@ -10,67 +10,6 @@ Options include the following:
## PyCharm/IntelliJ IDEA ## PyCharm/IntelliJ IDEA
There are several different ways you can use _Black_ from PyCharm:
1. Using the built-in _Black_ integration (PyCharm 2023.2 and later). This option is the
simplest to set up.
1. As local server using the BlackConnect plugin. This option formats the fastest. It
spins up {doc}`Black's HTTP server </usage_and_configuration/black_as_a_server>`, to
avoid the startup cost on subsequent formats.
1. As external tool.
1. As file watcher.
### Built-in _Black_ integration
1. Install `black`.
```console
$ pip install black
```
1. Go to `Preferences or Settings -> Tools -> Black` and configure _Black_ to your
liking.
### As local server
1. Install _Black_ with the `d` extra.
```console
$ pip install 'black[d]'
```
1. Install
[BlackConnect IntelliJ IDEs plugin](https://plugins.jetbrains.com/plugin/14321-blackconnect).
1. Open plugin configuration in PyCharm/IntelliJ IDEA
On macOS:
`PyCharm -> Preferences -> Tools -> BlackConnect`
On Windows / Linux / BSD:
`File -> Settings -> Tools -> BlackConnect`
1. In `Local Instance (shared between projects)` section:
1. Check `Start local blackd instance when plugin loads`.
1. Press the `Detect` button near `Path` input. The plugin should detect the `blackd`
executable.
1. In `Trigger Settings` section check `Trigger on code reformat` to enable code
reformatting with _Black_.
1. Format the currently opened file by selecting `Code -> Reformat Code` or using a
shortcut.
1. Optionally, to run _Black_ on every file save:
- In `Trigger Settings` section of plugin configuration check
`Trigger when saving changed files`.
### As external tool
1. Install `black`. 1. Install `black`.
```console ```console
@ -118,38 +57,13 @@ There are several different ways you can use _Black_ from PyCharm:
- Alternatively, you can set a keyboard shortcut by navigating to - Alternatively, you can set a keyboard shortcut by navigating to
`Preferences or Settings -> Keymap -> External Tools -> External Tools - Black`. `Preferences or Settings -> Keymap -> External Tools -> External Tools - Black`.
### As file watcher 1. Optionally, run _Black_ on every file save:
1. Install `black`. 1. Make sure you have the
```console
$ pip install black
```
1. Locate your `black` installation folder.
On macOS / Linux / BSD:
```console
$ which black
/usr/local/bin/black # possible location
```
On Windows:
```console
$ where black
%LocalAppData%\Programs\Python\Python36-32\Scripts\black.exe # possible location
```
Note that if you are using a virtual environment detected by PyCharm, this is an
unneeded step. In this case the path to `black` is `$PyInterpreterDirectory$/black`.
1. Make sure you have the
[File Watchers](https://plugins.jetbrains.com/plugin/7177-file-watchers) plugin [File Watchers](https://plugins.jetbrains.com/plugin/7177-file-watchers) plugin
installed. installed.
1. Go to `Preferences or Settings -> Tools -> File Watchers` and click `+` to add a new 1. Go to `Preferences or Settings -> Tools -> File Watchers` and click `+` to add a
watcher: new watcher:
- Name: Black - Name: Black
- File type: Python - File type: Python
- Scope: Project Files - Scope: Project Files
@ -158,57 +72,42 @@ There are several different ways you can use _Black_ from PyCharm:
- Output paths to refresh: `$FilePath$` - Output paths to refresh: `$FilePath$`
- Working directory: `$ProjectFileDir$` - Working directory: `$ProjectFileDir$`
- In Advanced Options - In Advanced Options
- Uncheck "Auto-save edited files to trigger the watcher" - Uncheck "Auto-save edited files to trigger the watcher"
- Uncheck "Trigger the watcher on external changes" - Uncheck "Trigger the watcher on external changes"
## Wing IDE ## Wing IDE
Wing IDE supports `black` via **Preference Settings** for system wide settings and Wing supports black via the OS Commands tool, as explained in the Wing documentation on
**Project Properties** for per-project or workspace specific settings, as explained in [pep8 formatting](https://wingware.com/doc/edit/pep8). The detailed procedure is:
the Wing documentation on
[Auto-Reformatting](https://wingware.com/doc/edit/auto-reformatting). The detailed
procedure is:
### Prerequistes 1. Install `black`.
- Wing IDE version 8.0+
- Install `black`.
```console ```console
$ pip install black $ pip install black
``` ```
- Make sure it runs from the command line, e.g. 1. Make sure it runs from the command line, e.g.
```console ```console
$ black --help $ black --help
``` ```
### Preference Settings 1. In Wing IDE, activate the **OS Commands** panel and define the command **black** to
execute black on the currently selected file:
If you want Wing IDE to always reformat with `black` for every project, follow these - Use the Tools -> OS Commands menu selection
steps: - click on **+** in **OS Commands** -> New: Command line..
- Title: black
- Command Line: black %s
- I/O Encoding: Use Default
- Key Binding: F1
- [x] Raise OS Commands when executed
- [x] Auto-save files before execution
- [x] Line mode
1. In menubar navigate to `Edit -> Preferences -> Editor -> Reformatting`. 1. Select a file in the editor and press **F1** , or whatever key binding you selected
in step 3, to reformat the file.
1. Set **Auto-Reformat** from `disable` (default) to `Line after edit` or
`Whole files before save`.
1. Set **Reformatter** from `PEP8` (default) to `Black`.
### Project Properties
If you want to just reformat for a specific project and not intervene with Wing IDE
global setting, follow these steps:
1. In menubar navigate to `Project -> Project Properties -> Options`.
1. Set **Auto-Reformat** from `Use Preferences setting` (default) to `Line after edit`
or `Whole files before save`.
1. Set **Reformatter** from `Use Preferences setting` (default) to `Black`.
## Vim ## Vim
@ -217,61 +116,23 @@ global setting, follow these steps:
Commands and shortcuts: Commands and shortcuts:
- `:Black` to format the entire file (ranges not supported); - `:Black` to format the entire file (ranges not supported);
- you can optionally pass `target_version=<version>` with the same values as in the
command line.
- `:BlackUpgrade` to upgrade _Black_ inside the virtualenv; - `:BlackUpgrade` to upgrade _Black_ inside the virtualenv;
- `:BlackVersion` to get the current version of _Black_ in use. - `:BlackVersion` to get the current version of _Black_ inside the virtualenv.
Configuration: Configuration:
- `g:black_fast` (defaults to `0`) - `g:black_fast` (defaults to `0`)
- `g:black_linelength` (defaults to `88`) - `g:black_linelength` (defaults to `88`)
- `g:black_skip_string_normalization` (defaults to `0`) - `g:black_skip_string_normalization` (defaults to `0`)
- `g:black_skip_magic_trailing_comma` (defaults to `0`)
- `g:black_virtualenv` (defaults to `~/.vim/black` or `~/.local/share/nvim/black`) - `g:black_virtualenv` (defaults to `~/.vim/black` or `~/.local/share/nvim/black`)
- `g:black_use_virtualenv` (defaults to `1`)
- `g:black_target_version` (defaults to `""`)
- `g:black_quiet` (defaults to `0`) - `g:black_quiet` (defaults to `0`)
- `g:black_preview` (defaults to `0`)
#### Installation
This plugin **requires Vim 7.0+ built with Python 3.9+ support**. It needs Python 3.9 to
be able to run _Black_ inside the Vim process which is much faster than calling an
external command.
##### `vim-plug`
To install with [vim-plug](https://github.com/junegunn/vim-plug): To install with [vim-plug](https://github.com/junegunn/vim-plug):
_Black_'s `stable` branch tracks official version updates, and can be used to simply
follow the most recent stable version.
``` ```
Plug 'psf/black', { 'branch': 'stable' } Plug 'psf/black', { 'branch': 'stable' }
``` ```
Another option which is a bit more explicit and offers more control is to use
`vim-plug`'s `tag` option with a shell wildcard. This will resolve to the latest tag
which matches the given pattern.
The following matches all stable versions (see the
[Release Process](../contributing/release_process.md) section for documentation of
version scheme used by Black):
```
Plug 'psf/black', { 'tag': '*.*.*' }
```
and the following demonstrates pinning to a specific year's stable style (2022 in this
case):
```
Plug 'psf/black', { 'tag': '22.*.*' }
```
##### Vundle
or with [Vundle](https://github.com/VundleVim/Vundle.vim): or with [Vundle](https://github.com/VundleVim/Vundle.vim):
``` ```
@ -285,14 +146,6 @@ $ cd ~/.vim/bundle/black
$ git checkout origin/stable -b stable $ git checkout origin/stable -b stable
``` ```
##### Arch Linux
On Arch Linux, the plugin is shipped with the
[`python-black`](https://archlinux.org/packages/extra/any/python-black/) package, so you
can start using it in Vim after install with no additional setup.
##### Vim 8 Native Plugin Management
or you can copy the plugin files from or you can copy the plugin files from
[plugin/black.vim](https://github.com/psf/black/blob/stable/plugin/black.vim) and [plugin/black.vim](https://github.com/psf/black/blob/stable/plugin/black.vim) and
[autoload/black.vim](https://github.com/psf/black/blob/stable/autoload/black.vim). [autoload/black.vim](https://github.com/psf/black/blob/stable/autoload/black.vim).
@ -307,7 +160,9 @@ curl https://raw.githubusercontent.com/psf/black/stable/autoload/black.vim -o ~/
Let me know if this requires any changes to work with Vim 8's builtin `packadd`, or Let me know if this requires any changes to work with Vim 8's builtin `packadd`, or
Pathogen, and so on. Pathogen, and so on.
#### Usage This plugin **requires Vim 7.0+ built with Python 3.6+ support**. It needs Python 3.6 to
be able to run _Black_ inside the Vim process which is much faster than calling an
external command.
On first run, the plugin creates its own virtualenv using the right Python version and On first run, the plugin creates its own virtualenv using the right Python version and
automatically installs _Black_. You can upgrade it later by calling `:BlackUpgrade` and automatically installs _Black_. You can upgrade it later by calling `:BlackUpgrade` and
@ -317,25 +172,10 @@ If you need to do anything special to make your virtualenv work and install _Bla
example you want to run a version from main), create a virtualenv manually and point example you want to run a version from main), create a virtualenv manually and point
`g:black_virtualenv` to it. The plugin will use it. `g:black_virtualenv` to it. The plugin will use it.
If you would prefer to use the system installation of _Black_ rather than a virtualenv, To run _Black_ on save, add the following line to `.vimrc` or `init.vim`:
then add this to your vimrc:
``` ```
let g:black_use_virtualenv = 0 autocmd BufWritePre *.py execute ':Black'
```
Note that the `:BlackUpgrade` command is only usable and useful with a virtualenv, so
when the virtualenv is not in use, `:BlackUpgrade` is disabled. If you need to upgrade
the system installation of _Black_, then use your system package manager or pip--
whatever tool you used to install _Black_ originally.
To run _Black_ on save, add the following lines to `.vimrc` or `init.vim`:
```
augroup black_on_save
autocmd!
autocmd BufWritePre *.py Black
augroup end
``` ```
To run _Black_ on a key press (e.g. F9 below), add this: To run _Black_ on a key press (e.g. F9 below), add this:
@ -344,6 +184,60 @@ To run _Black_ on a key press (e.g. F9 below), add this:
nnoremap <F9> :Black<CR> nnoremap <F9> :Black<CR>
``` ```
**How to get Vim with Python 3.6?** On Ubuntu 17.10 Vim comes with Python 3.6 by
default. On macOS with Homebrew run: `brew install vim`. When building Vim from source,
use: `./configure --enable-python3interp=yes`. There's many guides online how to do
this.
**I get an import error when using _Black_ from a virtual environment**: If you get an
error message like this:
```text
Traceback (most recent call last):
File "<string>", line 63, in <module>
File "/home/gui/.vim/black/lib/python3.7/site-packages/black.py", line 45, in <module>
from typed_ast import ast3, ast27
File "/home/gui/.vim/black/lib/python3.7/site-packages/typed_ast/ast3.py", line 40, in <module>
from typed_ast import _ast3
ImportError: /home/gui/.vim/black/lib/python3.7/site-packages/typed_ast/_ast3.cpython-37m-x86_64-linux-gnu.so: undefined symbool: PyExc_KeyboardInterrupt
```
Then you need to install `typed_ast` and `regex` directly from the source code. The
error happens because `pip` will download [Python wheels](https://pythonwheels.com/) if
they are available. Python wheels are a new standard of distributing Python packages and
packages that have Cython and extensions written in C are already compiled, so the
installation is much more faster. The problem here is that somehow the Python
environment inside Vim does not match with those already compiled C extensions and these
kind of errors are the result. Luckily there is an easy fix: installing the packages
from the source code.
The two packages that cause the problem are:
- [regex](https://pypi.org/project/regex/)
- [typed-ast](https://pypi.org/project/typed-ast/)
Now remove those two packages:
```console
$ pip uninstall regex typed-ast -y
```
And now you can install them with:
```console
$ pip install --no-binary :all: regex typed-ast
```
The C extensions will be compiled and now Vim's Python environment will match. Note that
you need to have the GCC compiler and the Python development files installed (on
Ubuntu/Debian do `sudo apt-get install build-essential python3-dev`).
If you later want to update _Black_, you should do it like this:
```console
$ pip install -U black --no-binary regex,typed-ast
```
### With ALE ### With ALE
1. Install [`ale`](https://github.com/dense-analysis/ale) 1. Install [`ale`](https://github.com/dense-analysis/ale)
@ -389,20 +283,17 @@ close and reopen your File, _Black_ will be done with its job.
## Visual Studio Code ## Visual Studio Code
- Use the Use the
[Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) [Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python)
([instructions](https://code.visualstudio.com/docs/python/formatting)). ([instructions](https://code.visualstudio.com/docs/python/editing#_formatting)).
- Alternatively the pre-release ## SublimeText 3
[Black Formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter)
extension can be used which runs a [Language Server Protocol](https://langserver.org/)
server for Black. Formatting is much more responsive using this extension, **but the
minimum supported version of Black is 22.3.0**.
## SublimeText Use [sublack plugin](https://github.com/jgirardet/sublack).
For SublimeText 3, use [sublack plugin](https://github.com/jgirardet/sublack). For ## Jupyter Notebook Magic
higher versions, it is recommended to use [LSP](#python-lsp-server) as documented below.
Use [blackcellmagic](https://github.com/csurfer/blackcellmagic).
## Python LSP Server ## Python LSP Server
@ -432,4 +323,4 @@ hook global WinSetOption filetype=python %{
## Thonny ## Thonny
Use [Thonny-black-formatter](https://pypi.org/project/thonny-black-formatter/). Use [Thonny-black-code-format](https://github.com/Franccisco/thonny-black-code-format).

View File

@ -8,8 +8,8 @@ environment. Great for enforcing that your code matches the _Black_ code style.
This action is known to support all GitHub-hosted runner OSes. In addition, only This action is known to support all GitHub-hosted runner OSes. In addition, only
published versions of _Black_ are supported (i.e. whatever is available on PyPI). published versions of _Black_ are supported (i.e. whatever is available on PyPI).
Finally, this action installs _Black_ with the `colorama` extra so the `--color` flag Finally, this action installs _Black_ with both the `colorama` and `python2` extras so
should work fine. the `--color` flag and formatting Python 2 code are supported.
## Usage ## Usage
@ -24,7 +24,7 @@ jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- uses: psf/black@stable - uses: psf/black@stable
``` ```
@ -32,24 +32,12 @@ We recommend the use of the `@stable` tag, but per version tags also exist if yo
that. Note that the action's version you select is independent of the version of _Black_ that. Note that the action's version you select is independent of the version of _Black_
the action will use. the action will use.
The version of _Black_ the action will use can be configured via `version` or read from The version of _Black_ the action will use can be configured via `version`. The action
the `pyproject.toml` file. `version` can be any defaults to the latest release available on PyPI. Only versions available from PyPI are
[valid version specifier](https://packaging.python.org/en/latest/glossary/#term-Version-Specifier) supported, so no commit SHAs or branch names.
or just the version number if you want an exact version. To read the version from the
`pyproject.toml` file instead, set `use_pyproject` to `true`. This will first look into
the `tool.black.required-version` field, then the `dependency-groups` table, then the
`project.dependencies` array and finally the `project.optional-dependencies` table. The
action defaults to the latest release available on PyPI. Only versions available from
PyPI are supported, so no commit SHAs or branch names.
If you want to include Jupyter Notebooks, _Black_ must be installed with the `jupyter`
extra. Installing the extra and including Jupyter Notebook files can be configured via
`jupyter` (default is `false`).
You can also configure the arguments passed to _Black_ via `options` (defaults to You can also configure the arguments passed to _Black_ via `options` (defaults to
`'--check --diff'`) and `src` (default is `'.'`). Please note that the `'--check --diff'`) and `src` (default is `'.'`)
[`--check` flag](labels/exit-code) is required so that the workflow fails if _Black_
finds files that need to be formatted.
Here's an example configuration: Here's an example configuration:
@ -58,33 +46,5 @@ Here's an example configuration:
with: with:
options: "--check --verbose" options: "--check --verbose"
src: "./src" src: "./src"
jupyter: true
version: "21.5b1" version: "21.5b1"
``` ```
If you want to match versions covered by Black's
[stability policy](labels/stability-policy), you can use the compatible release operator
(`~=`):
```yaml
- uses: psf/black@stable
with:
options: "--check --verbose"
src: "./src"
version: "~= 22.0"
```
If you want to read the version from `pyproject.toml`, set `use_pyproject` to `true`.
Note that this requires Python >= 3.11, so using the setup-python action may be
required, for example:
```yaml
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: psf/black@stable
with:
options: "--check --verbose"
src: "./src"
use_pyproject: true
```

View File

@ -1,31 +0,0 @@
# Integrations
```{toctree}
---
hidden:
---
editors
github_actions
source_version_control
```
_Black_ can be integrated into many environments, providing a better and smoother
experience. Documentation for integrating _Black_ with a tool can be found for the
following areas:
- {doc}`Editor / IDE <./editors>`
- {doc}`GitHub Actions <./github_actions>`
- {doc}`Source version control <./source_version_control>`
Editors and tools not listed will require external contributions.
Patches welcome! ✨ 🍰 ✨
Any tool can pipe code through _Black_ using its stdio mode (just
[use `-` as the file name](https://www.tldp.org/LDP/abs/html/special-chars.html#DASHREF2)).
The formatted code will be returned on stdout (unless `--check` was passed). _Black_
will still emit messages on stderr but that shouldn't affect your use case.
This can be used for example with PyCharm's or IntelliJ's
[File Watchers](https://www.jetbrains.com/help/pycharm/file-watchers.html).

View File

@ -0,0 +1,28 @@
Integrations
============
.. toctree::
:hidden:
editors
github_actions
source_version_control
*Black* can be integrated into many environments, providing a better and smoother experience. Documentation for integrating *Black* with a tool can be found for the
following areas:
- :doc:`Editor / IDE <./editors>`
- :doc:`GitHub Actions <./github_actions>`
- :doc:`Source version control <./source_version_control>`
Editors and tools not listed will require external contributions.
Patches welcome! ✨ 🍰 ✨
Any tool can pipe code through *Black* using its stdio mode (just
`use \`-\` as the file name <https://www.tldp.org/LDP/abs/html/special-chars.html#DASHREF2>`_).
The formatted code will be returned on stdout (unless ``--check`` was passed). *Black*
will still emit messages on stderr but that shouldn't affect your use case.
This can be used for example with PyCharm's or IntelliJ's
`File Watchers <https://www.jetbrains.com/help/pycharm/file-watchers.html>`_.

View File

@ -6,48 +6,22 @@ Use [pre-commit](https://pre-commit.com/). Once you
```yaml ```yaml
repos: repos:
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster - repo: https://github.com/psf/black
- repo: https://github.com/psf/black-pre-commit-mirror rev: 21.8b0
rev: 25.1.0
hooks: hooks:
- id: black - id: black
# It is recommended to specify the latest version of Python language_version: python3 # Should be a command that runs python3.6+
# supported by your project here, or alternatively use
# pre-commit's default_language_version, see
# https://pre-commit.com/#top_level-default_language_version
language_version: python3.11
``` ```
Feel free to switch out the `rev` value to a different version of Black. Feel free to switch out the `rev` value to something else, like another
[tag/version][black-tags] or even a specific commit. Although we discourage the use of
Note if you'd like to use a specific commit in `rev`, you'll need to swap the repo
specified from the mirror to https://github.com/psf/black. We discourage the use of
branches or other mutable refs since the hook [won't auto update as you may branches or other mutable refs since the hook [won't auto update as you may
expect][pre-commit-mutable-rev]. expect][pre-commit-mutable-rev].
## Jupyter Notebooks If you want support for Jupyter Notebooks as well, then replace `id: black` with
`id: black-jupyter` (though note that it's only available from version `21.8b0`
There is an alternate hook `black-jupyter` that expands the targets of `black` to onwards).
include Jupyter Notebooks. To use this hook, simply replace the hook's `id: black` with
`id: black-jupyter` in the `.pre-commit-config.yaml`:
```yaml
repos:
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 25.1.0
hooks:
- id: black-jupyter
# It is recommended to specify the latest version of Python
# supported by your project here, or alternatively use
# pre-commit's default_language_version, see
# https://pre-commit.com/#top_level-default_language_version
language_version: python3.11
```
```{note}
The `black-jupyter` hook became available in version 21.8b0.
```
[black-tags]: https://github.com/psf/black/tags
[pre-commit-mutable-rev]: [pre-commit-mutable-rev]:
https://pre-commit.com/#using-the-latest-version-for-a-repository https://pre-commit.com/#using-the-latest-version-for-a-repository

View File

@ -1,9 +0,0 @@
---
orphan: true
---
# License
```{include} ../LICENSE
```

6
docs/license.rst Normal file
View File

@ -0,0 +1,6 @@
:orphan:
License
=======
.. include:: ../LICENSE

View File

@ -1,9 +1,6 @@
# Used by ReadTheDocs; pinned requirements for stability. # Used by ReadTheDocs; pinned requirements for stability.
myst-parser==4.0.1 myst-parser==0.15.1
Sphinx==8.2.3 Sphinx==4.1.2
# Older versions break Sphinx even though they're declared to be supported. sphinxcontrib-programoutput==0.17
docutils==0.21.2 sphinx_copybutton==0.4.0
sphinxcontrib-programoutput==0.18
sphinx_copybutton==0.5.2
furo==2024.8.6

View File

@ -2,21 +2,20 @@
## Code style ## Code style
_Black_ aims for consistency, generality, readability and reducing git diffs. Similar _Black_ reformats entire files in place. Style configuration options are deliberately
language constructs are formatted with similar rules. Style configuration options are limited and rarely added. It doesn't take previous formatting into account, except for
deliberately limited and rarely added. Previous formatting is taken into account as the magic trailing comma and preserving newlines. It doesn't reformat blocks that start
little as possible, with rare exceptions like the magic trailing comma. The coding style with `# fmt: off` and end with `# fmt: on`, or lines that ends with `# fmt: skip`.
used by _Black_ can be viewed as a strict subset of PEP 8. `# fmt: on/off` have to be on the same level of indentation. It also recognizes
[YAPF](https://github.com/google/yapf)'s block comments to the same effect, as a
This document describes the current formatting style. If you're interested in trying out courtesy for straddling code.
where the style is heading, see [future style](./future_style.md) and try running
`black --preview`.
### How _Black_ wraps lines ### How _Black_ wraps lines
_Black_ ignores previous formatting and applies uniform horizontal and vertical _Black_ ignores previous formatting and applies uniform horizontal and vertical
whitespace to your code. The rules for horizontal whitespace can be summarized as: do whitespace to your code. The rules for horizontal whitespace can be summarized as: do
whatever makes `pycodestyle` happy. whatever makes `pycodestyle` happy. The coding style used by _Black_ can be viewed as a
strict subset of PEP 8.
As for vertical whitespace, _Black_ tries to render one full expression or simple As for vertical whitespace, _Black_ tries to render one full expression or simple
statement per line. If this fits the allotted line length, great. statement per line. If this fits the allotted line length, great.
@ -78,19 +77,6 @@ def very_important_function(
... ...
``` ```
If a data structure literal (tuple, list, set, dict) or a line of "from" imports cannot
fit in the allotted length, it's always split into one element per line. This minimizes
diffs as well as enables readers of code to find which commit introduced a particular
entry. This also makes _Black_ compatible with
[isort](../guides/using_black_with_other_tools.md#isort) with the ready-made `black`
profile or manual configuration.
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
clear delimiter between two distinct sections of the code that otherwise share the same
indentation level (like the arguments list and the docstring in the example above).
(labels/why-no-backslashes)= (labels/why-no-backslashes)=
_Black_ prefers parentheses over backslashes, and will remove backslashes if found. _Black_ prefers parentheses over backslashes, and will remove backslashes if found.
@ -133,7 +119,18 @@ If you're reaching for backslashes, that's a clear signal that you can do better
slightly refactor your code. I hope some of the examples above show you that there are slightly refactor your code. I hope some of the examples above show you that there are
many ways in which you can do it. many ways in which you can do it.
(labels/line-length)= 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
clear delimiter between two distinct sections of the code that otherwise share the same
indentation level (like the arguments list and the docstring in the example above).
If a data structure literal (tuple, list, set, dict) or a line of "from" imports cannot
fit in the allotted length, it's always split into one element per line. This minimizes
diffs as well as enables readers of code to find which commit introduced a particular
entry. This also makes _Black_ compatible with
[isort](../guides/using_black_with_other_tools.md#isort) with the ready-made `black`
profile or manual configuration.
### Line length ### Line length
@ -143,7 +140,7 @@ significantly shorter files than sticking with 80 (the most popular), or even 79
by the standard library). In general, by the standard library). In general,
[90-ish seems like the wise choice](https://youtu.be/wf-BqAjZb8M?t=260). [90-ish seems like the wise choice](https://youtu.be/wf-BqAjZb8M?t=260).
If you're paid by the lines of code you write, you can pass `--line-length` with a lower If you're paid by the line of code you write, you can pass `--line-length` with a lower
number. _Black_ will try to respect that. However, sometimes it won't be able to without number. _Black_ will try to respect that. However, sometimes it won't be able to without
breaking other rules. In those rare cases, auto-formatted code will exceed your allotted breaking other rules. In those rare cases, auto-formatted code will exceed your allotted
limit. limit.
@ -153,10 +150,33 @@ harder to work with line lengths exceeding 100 characters. It also adversely aff
side-by-side diff review on typical screen resolutions. Long lines also make it harder side-by-side diff review on typical screen resolutions. Long lines also make it harder
to present code neatly in documentation or talk slides. to present code neatly in documentation or talk slides.
#### Flake8 and other linters If you're using Flake8, you can bump `max-line-length` to 88 and mostly forget about it.
However, it's better if you use [Bugbear](https://github.com/PyCQA/flake8-bugbear)'s
B950 warning instead of E501, and bump the max line length to 88 (or the `--line-length`
you used for black), which will align more with black's _"try to respect
`--line-length`, but don't become crazy if you can't"_. You'd do it like this:
See [Using _Black_ with other tools](../guides/using_black_with_other_tools.md) about ```ini
linter compatibility. [flake8]
max-line-length = 88
...
select = C,E,F,W,B,B950
extend-ignore = E203, E501
```
Explanation of why E203 is disabled can be found further in this documentation. And if
you're curious about the reasoning behind B950,
[Bugbear's documentation](https://github.com/PyCQA/flake8-bugbear#opinionated-warnings)
explains it. The tl;dr is "it's like highway speed limits, we won't bother you if you
overdo it by a few km/h".
**If you're looking for a minimal, black-compatible flake8 configuration:**
```ini
[flake8]
max-line-length = 88
extend-ignore = E203
```
### Empty lines ### Empty lines
@ -168,35 +188,6 @@ lines on module level left by the original editors, except when they're within
parenthesized expressions. Since such expressions are always reformatted to fit minimal parenthesized expressions. Since such expressions are always reformatted to fit minimal
space, this whitespace is lost. space, this whitespace is lost.
```python
# in:
def function(
some_argument: int,
other_argument: int = 5,
) -> EmptyLineInParenWillBeDeleted:
print("One empty line above me will be kept!")
def this_is_okay_too():
print("No empty line here")
# out:
def function(
some_argument: int,
other_argument: int = 5,
) -> EmptyLineInParenWillBeDeleted:
print("One empty line above me will be kept!")
def this_is_okay_too():
print("No empty line here")
```
It will also insert proper spacing before and after function definitions. It's one line It will also insert proper spacing before and after function definitions. It's one line
before and after inner functions and two lines before and after module-level functions before and after inner functions and two lines before and after module-level functions
and classes. _Black_ will not put empty lines between function/class definitions and and classes. _Black_ will not put empty lines between function/class definitions and
@ -213,12 +204,11 @@ required due to an inner function starting immediately after.
_Black_ does not format comment contents, but it enforces two spaces between code and a _Black_ does not format comment contents, but it enforces two spaces between code and a
comment on the same line, and a space before the comment text begins. Some types of comment on the same line, and a space before the comment text begins. Some types of
comments that require specific spacing rules are respected: shebangs (`#! comment`), doc comments that require specific spacing rules are respected: doc comments (`#: comment`),
comments (`#: comment`), section comments with long runs of hashes, and Spyder cells. section comments with long runs of hashes, and Spyder cells. Non-breaking spaces after
Non-breaking spaces after hashes are also preserved. Comments may sometimes be moved hashes are also preserved. Comments may sometimes be moved because of formatting
because of formatting changes, which can break tools that assign special meaning to changes, which can break tools that assign special meaning to them. See
them. See [AST before and after formatting](#ast-before-and-after-formatting) for more [AST before and after formatting](#ast-before-and-after-formatting) for more discussion.
discussion.
### Trailing commas ### Trailing commas
@ -237,23 +227,16 @@ A pre-existing trailing comma informs _Black_ to always explode contents of the
bracket pair into one item per line. Read more about this in the bracket pair into one item per line. Read more about this in the
[Pragmatism](#pragmatism) section below. [Pragmatism](#pragmatism) section below.
(labels/strings)=
### Strings ### Strings
_Black_ prefers double quotes (`"` and `"""`) over single quotes (`'` and `'''`). It _Black_ prefers double quotes (`"` and `"""`) over single quotes (`'` and `'''`). It
will replace the latter with the former as long as it does not result in more backslash will replace the latter with the former as long as it does not result in more backslash
escapes than before. escapes than before.
_Black_ also standardizes string prefixes. Prefix characters are made lowercase with the _Black_ also standardizes string prefixes, making them always lowercase. On top of that,
exception of [capital "R" prefixes](#rstrings-and-rstrings), unicode literal markers if your code is already Python 3.6+ only or it's using the `unicode_literals` future
(`u`) are removed because they are meaningless in Python 3, and in the case of multiple import, _Black_ will remove `u` from the string prefix as it is meaningless in those
characters "r" is put first as in spoken language: "raw f-string". scenarios.
Another area where Python allows multiple ways to format a string is escape sequences.
For example, `"\uabcd"` and `"\uABCD"` evaluate to the same string. _Black_ normalizes
such escape sequences to lowercase, but uses uppercase for `\N` named character escapes,
such as `"\N{MEETEI MAYEK LETTER HUK}"`.
The main reason to standardize on a single form of quotes is aesthetics. Having one kind The main reason to standardize on a single form of quotes is aesthetics. Having one kind
of quotes everywhere reduces reader distraction. It will also enable a future version of of quotes everywhere reduces reader distraction. It will also enable a future version of
@ -277,6 +260,16 @@ If you are adopting _Black_ in a large project with pre-existing string conventi
you can pass `--skip-string-normalization` on the command line. This is meant as an you can pass `--skip-string-normalization` on the command line. This is meant as an
adoption helper, avoid using this for new projects. adoption helper, avoid using this for new projects.
(labels/experimental-string)=
As an experimental option (can be enabled by `--experimental-string-processing`),
_Black_ splits long strings (using parentheses where appropriate) and merges short ones.
When split, parts of f-strings that don't need formatting are converted to plain
strings. User-made splits are respected when they do not exceed the line length limit.
Line continuation backslashes are converted into parenthesized strings. Unnecessary
parentheses are stripped. Because the functionality is experimental, feedback and issue
reports are highly encouraged!
_Black_ also processes docstrings. Firstly the indentation of docstrings is corrected _Black_ also processes docstrings. Firstly the indentation of docstrings is corrected
for both quotations and the text within, although relative indentation in the text is for both quotations and the text within, although relative indentation in the text is
preserved. Superfluous trailing whitespace on each line and unnecessary new lines at the preserved. Superfluous trailing whitespace on each line and unnecessary new lines at the
@ -288,7 +281,8 @@ removed.
_Black_ standardizes most numeric literals to use lowercase letters for the syntactic _Black_ standardizes most numeric literals to use lowercase letters for the syntactic
parts and uppercase letters for the digits themselves: `0xAB` instead of `0XAB` and parts and uppercase letters for the digits themselves: `0xAB` instead of `0XAB` and
`1e10` instead of `1E10`. `1e10` instead of `1E10`. Python 2 long literals are styled as `2L` instead of `2l` to
avoid confusion between `l` and `1`.
### Line breaks & binary operators ### Line breaks & binary operators
@ -297,26 +291,6 @@ multiple lines. This is so that _Black_ is compliant with the recent changes in
[PEP 8](https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator) [PEP 8](https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator)
style guide, which emphasizes that this approach improves readability. style guide, which emphasizes that this approach improves readability.
Almost all operators will be surrounded by single spaces, the only exceptions are unary
operators (`+`, `-`, and `~`), and power operators when both operands are simple. For
powers, an operand is considered simple if it's only a NAME, numeric CONSTANT, or
attribute access (chained attribute access is allowed), with or without a preceding
unary operator.
```python
# For example, these won't be surrounded by whitespace
a = x**y
b = config.base**5.2
c = config.base**runtime.config.exponent
d = 2**5
e = 2**~5
# ... but these will be surrounded by whitespace
f = 2 ** get_exponent()
g = get_x() ** get_y()
h = config['base'] ** 2
```
### Slices ### Slices
PEP 8 PEP 8
@ -409,22 +383,16 @@ recommended code style for those files is more terse than PEP 8:
_Black_ enforces the above rules. There are additional guidelines for formatting `.pyi` _Black_ enforces the above rules. There are additional guidelines for formatting `.pyi`
file that are not enforced yet but might be in a future version of the formatter: file that are not enforced yet but might be in a future version of the formatter:
- all function bodies should be empty (contain `...` instead of the body);
- do not use docstrings;
- prefer `...` over `pass`; - prefer `...` over `pass`;
- for arguments with a default, use `...` instead of the actual default;
- avoid using string literals in type annotations, stub files support forward references - avoid using string literals in type annotations, stub files support forward references
natively (like Python 3.7 code with `from __future__ import annotations`); natively (like Python 3.7 code with `from __future__ import annotations`);
- use variable annotations instead of type comments, even for stubs that target older - use variable annotations instead of type comments, even for stubs that target older
versions of Python. versions of Python;
- for arguments that default to `None`, use `Optional[]` explicitly;
### Line endings - use `float` instead of `Union[int, float]`.
_Black_ will normalize line endings (`\n` or `\r\n`) based on the first line ending of
the file.
### Form feed characters
_Black_ will retain form feed characters on otherwise empty lines at the module level.
Only one form feed is retained for a group of consecutive empty lines. Where there are
two empty lines in a row, the form feed is placed on the second line.
## Pragmatism ## Pragmatism
@ -434,8 +402,6 @@ there were not many users anyway. Not many edge cases were reported. As a mature
_Black_ does make some exceptions to rules it otherwise holds. This section documents _Black_ does make some exceptions to rules it otherwise holds. This section documents
what those exceptions are and why this is the case. what those exceptions are and why this is the case.
(labels/magic-trailing-comma)=
### The magic trailing comma ### The magic trailing comma
_Black_ in general does not take existing formatting into account. _Black_ in general does not take existing formatting into account.
@ -472,19 +438,17 @@ default by (among others) GitHub and Visual Studio Code, differentiates between
r-strings and R-strings. The former are syntax highlighted as regular expressions while r-strings and R-strings. The former are syntax highlighted as regular expressions while
the latter are treated as true raw strings with no special semantics. the latter are treated as true raw strings with no special semantics.
(labels/ast-changes)=
### AST before and after formatting ### AST before and after formatting
When run with `--safe` (the default), _Black_ checks that the code before and after is When run with `--safe`, _Black_ checks that the code before and after is semantically
semantically equivalent. This check is done by comparing the AST of the source with the equivalent. This check is done by comparing the AST of the source with the AST of the
AST of the target. There are three limited cases in which the AST does differ: target. There are three limited cases in which the AST does differ:
1. _Black_ cleans up leading and trailing whitespace of docstrings, re-indenting them if 1. _Black_ cleans up leading and trailing whitespace of docstrings, re-indenting them if
needed. It's been one of the most popular user-reported features for the formatter to needed. It's been one of the most popular user-reported features for the formatter to
fix whitespace issues with docstrings. While the result is technically an AST fix whitespace issues with docstrings. While the result is technically an AST
difference, due to the various possibilities of forming docstrings, all real-world difference, due to the various possibilities of forming docstrings, all realtime use
uses of docstrings that we're aware of sanitize indentation and leading/trailing of docstrings that we're aware of sanitizes indentation and leading/trailing
whitespace anyway. whitespace anyway.
1. _Black_ manages optional parentheses for some statements. In the case of the `del` 1. _Black_ manages optional parentheses for some statements. In the case of the `del`

View File

@ -1,269 +1,42 @@
# The (future of the) Black code style # The (future of the) Black code style
## Preview style ```{warning}
Changes to this document often aren't tied and don't relate to releases of
(labels/preview-style)= _Black_. It's recommended that you read the latest version available.
Experimental, potentially disruptive style changes are gathered under the `--preview`
CLI flag. At the end of each year, these changes may be adopted into the default style,
as described in [The Black Code Style](index.md). Because the functionality is
experimental, feedback and issue reports are highly encouraged!
In the past, the preview style included some features with known bugs, so that we were
unable to move these features to the stable style. Therefore, such features are now
moved to the `--unstable` style. All features in the `--preview` style are expected to
make it to next year's stable style; features in the `--unstable` style will be
stabilized only if issues with them are fixed. If bugs are discovered in a `--preview`
feature, it is demoted to the `--unstable` style. To avoid thrash when a feature is
demoted from the `--preview` to the `--unstable` style, users can use the
`--enable-unstable-feature` flag to enable specific unstable features.
(labels/preview-features)=
Currently, the following features are included in the preview style:
- `always_one_newline_after_import`: Always force one blank line after import
statements, except when the line after the import is a comment or an import statement
- `wrap_long_dict_values_in_parens`: Add parentheses around long values in dictionaries
([see below](labels/wrap-long-dict-values))
- `fix_fmt_skip_in_one_liners`: Fix `# fmt: skip` behaviour on one-liner declarations,
such as `def foo(): return "mock" # fmt: skip`, where previously the declaration
would have been incorrectly collapsed.
(labels/unstable-features)=
The unstable style additionally includes the following features:
- `string_processing`: split long string literals and related changes
([see below](labels/string-processing))
- `multiline_string_handling`: more compact formatting of expressions involving
multiline strings ([see below](labels/multiline-string-handling))
- `hug_parens_with_braces_and_square_brackets`: more compact formatting of nested
brackets ([see below](labels/hug-parens))
(labels/wrap-long-dict-values)=
### Improved parentheses management in dicts
For dict literals with long values, they are now wrapped in parentheses. Unnecessary
parentheses are now removed. For example:
```python
my_dict = {
"a key in my dict": a_very_long_variable
* and_a_very_long_function_call()
/ 100000.0,
"another key": (short_value),
}
``` ```
will be changed to: ## Using backslashes for with statements
```python [Backslashes are bad and should be never be used](labels/why-no-backslashes) however
my_dict = { there is one exception: `with` statements using multiple context managers. Before Python
"a key in my dict": ( 3.9 Python's grammar does not allow organizing parentheses around the series of context
a_very_long_variable * and_a_very_long_function_call() / 100000.0 managers.
),
"another key": short_value, 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
``` ```
(labels/hug-parens)= So _Black_ will eventually format it like this:
### Improved multiline dictionary and list indentation for sole function parameter ```py3
with \
For better readability and less verticality, _Black_ now pairs parentheses ("(", ")") make_context_manager(1) as cm1, \
with braces ("{", "}") and square brackets ("[", "]") on the same line. For example: make_context_manager(2) as cm2, \
make_context_manager(3) as cm3, \
```python make_context_manager(4) as cm4 \
foo( :
[ ... # backslashes and an ugly stranded colon
1,
2,
3,
]
)
nested_array = [
[
1,
2,
3,
]
]
``` ```
will be changed to: Although when the target version is Python 3.9 or higher, _Black_ will use parentheses
instead since they're allowed in Python 3.9 and higher.
```python ## Improved string processing
foo([
1,
2,
3,
])
nested_array = [[ Currently, _Black_ does not split long strings to fit the line length limit. Currently,
1, there is [an experimental option](labels/experimental-string) to enable splitting
2, strings. We plan to enable this option by default once it is fully stable. This is
3, tracked in [this issue](https://github.com/psf/black/issues/2188).
]]
```
This also applies to list and dictionary unpacking:
```python
foo(
*[
a_long_function_name(a_long_variable_name)
for a_long_variable_name in some_generator
]
)
```
will become:
```python
foo(*[
a_long_function_name(a_long_variable_name)
for a_long_variable_name in some_generator
])
```
You can use a magic trailing comma to avoid this compacting behavior; by default,
_Black_ will not reformat the following code:
```python
foo(
[
1,
2,
3,
],
)
```
(labels/string-processing)=
### Improved string processing
_Black_ will split long string literals and merge short ones. Parentheses are used where
appropriate. When split, parts of f-strings that don't need formatting are converted to
plain strings. f-strings will not be merged if they contain internal quotes and it would
change their quotation mark style. User-made splits are respected when they do not
exceed the line length limit. Line continuation backslashes are converted into
parenthesized strings. Unnecessary parentheses are stripped. The stability and status of
this feature istracked in [this issue](https://github.com/psf/black/issues/2188).
(labels/multiline-string-handling)=
### Improved multiline string handling
_Black_ is smarter when formatting multiline strings, especially in function arguments,
to avoid introducing extra line breaks. Previously, it would always consider multiline
strings as not fitting on a single line. With this new feature, _Black_ looks at the
context around the multiline string to decide if it should be inlined or split to a
separate line. For example, when a multiline string is passed to a function, _Black_
will only split the multiline string if a line is too long or if multiple arguments are
being passed.
For example, _Black_ will reformat
```python
textwrap.dedent(
"""\
This is a
multiline string
"""
)
```
to:
```python
textwrap.dedent("""\
This is a
multiline string
""")
```
And:
```python
MULTILINE = """
foobar
""".replace(
"\n", ""
)
```
to:
```python
MULTILINE = """
foobar
""".replace("\n", "")
```
Implicit multiline strings are special, because they can have inline comments. Strings
without comments are merged, for example
```python
s = (
"An "
"implicit "
"multiline "
"string"
)
```
becomes
```python
s = "An implicit multiline string"
```
A comment on any line of the string (or between two string lines) will block the
merging, so
```python
s = (
"An " # Important comment concerning just this line
"implicit "
"multiline "
"string"
)
```
and
```python
s = (
"An "
"implicit "
# Comment in between
"multiline "
"string"
)
```
will not be merged. Having the comment after or before the string lines (but still
inside the parens) will merge the string. For example
```python
s = ( # Top comment
"An "
"implicit "
"multiline "
"string"
# Bottom comment
)
```
becomes
```python
s = ( # Top comment
"An implicit multiline string"
# Bottom comment
)
```

View File

@ -1,54 +0,0 @@
# The Black Code Style
```{toctree}
---
hidden:
---
Current style <current_style>
Future style <future_style>
```
_Black_ is a PEP 8 compliant opinionated formatter with its own style.
While keeping the style unchanged throughout releases has always been a goal, the
_Black_ code style isn't set in stone. It evolves to accommodate for new features in the
Python language and, occasionally, in response to user feedback. Large-scale style
preferences presented in {doc}`current_style` are very unlikely to change, but minor
style aspects and details might change according to the stability policy presented
below. Ongoing style considerations are tracked on GitHub with the
[style](https://github.com/psf/black/labels/T%3A%20style) issue label.
(labels/stability-policy)=
## Stability Policy
The following policy applies for the _Black_ code style, in non pre-release versions of
_Black_:
- If code has been formatted with _Black_, it will remain unchanged when formatted with
the same options using any other release in the same calendar year.
This means projects can safely use `black ~= 22.0` without worrying about formatting
changes disrupting their project in 2022. We may still fix bugs where _Black_ crashes
on some code, and make other improvements that do not affect formatting.
In rare cases, we may make changes affecting code that has not been previously
formatted with _Black_. For example, we have had bugs where we accidentally removed
some comments. Such bugs can be fixed without breaking the stability policy.
- The first release in a new calendar year _may_ contain formatting changes, although
these will be minimised as much as possible. This is to allow for improved formatting
enabled by newer Python language syntax as well as due to improvements in the
formatting logic.
- The `--preview` and `--unstable` flags are exempt from this policy. There are no
guarantees around the stability of the output with these flags passed into _Black_.
They are intended for allowing experimentation with proposed changes to the _Black_
code style. The `--preview` style at the end of a year should closely match the stable
style for the next year, but we may always make changes.
Documentation for both the current and future styles can be found:
- {doc}`current_style`
- {doc}`future_style`

View File

@ -0,0 +1,19 @@
The Black Code Style
====================
.. toctree::
:hidden:
Current style <current_style>
Future style <future_style>
*Black* is a PEP 8 compliant opinionated formatter with its own style.
It should be noted that while keeping the style unchanged throughout releases is a
goal, the *Black* code style isn't set in stone. Sometimes it's modified in response to
user feedback or even changes to the Python language!
Documentation for both the current and future styles can be found:
- :doc:`current_style`
- :doc:`future_style`

View File

@ -4,15 +4,10 @@
protocol. The main benefit of using it is to avoid the cost of starting up a new _Black_ protocol. The main benefit of using it is to avoid the cost of starting up a new _Black_
process every time you want to blacken a file. process every time you want to blacken a file.
```{warning}
`blackd` should not be run as a publicly accessible server as there are no security
precautions in place to prevent abuse. **It is intended for local use only**.
```
## Usage ## Usage
`blackd` is not packaged alongside _Black_ by default because it has additional `blackd` is not packaged alongside _Black_ by default because it has additional
dependencies. You will need to execute `pip install 'black[d]'` to install it. dependencies. You will need to execute `pip install black[d]` to install it.
You can start the server on the default port, binding only to the local interface by You can start the server on the default port, binding only to the local interface by
running `blackd`. You will see a single line mentioning the server's version, and the running `blackd`. You will see a single line mentioning the server's version, and the
@ -50,24 +45,12 @@ is rejected with `HTTP 501` (Not Implemented).
The headers controlling how source code is formatted are: The headers controlling how source code is formatted are:
- `X-Line-Length`: corresponds to the `--line-length` command line flag. - `X-Line-Length`: corresponds to the `--line-length` command line flag.
- `X-Skip-Source-First-Line`: corresponds to the `--skip-source-first-line` command line
flag. If present and its value is not an empty string, the first line of the source
code will be ignored.
- `X-Skip-String-Normalization`: corresponds to the `--skip-string-normalization` - `X-Skip-String-Normalization`: corresponds to the `--skip-string-normalization`
command line flag. If present and its value is not the empty string, no string command line flag. If present and its value is not the empty string, no string
normalization will be performed. normalization will be performed.
- `X-Skip-Magic-Trailing-Comma`: corresponds to the `--skip-magic-trailing-comma` - `X-Skip-Magic-Trailing-Comma`: corresponds to the `--skip-magic-trailing-comma`
command line flag. If present and its value is not an empty string, trailing commas command line flag. If present and its value is not the empty string, trailing commas
will not be used as a reason to split lines. will not be used as a reason to split lines.
- `X-Preview`: corresponds to the `--preview` command line flag. If present and its
value is not an empty string, experimental and potentially disruptive style changes
will be used.
- `X-Unstable`: corresponds to the `--unstable` command line flag. If present and its
value is not an empty string, experimental style changes that are known to be buggy
will be used.
- `X-Enable-Unstable-Feature`: corresponds to the `--enable-unstable-feature` flag. The
contents of the flag must be a comma-separated list of unstable features to be
enabled. Example: `X-Enable-Unstable-Feature: feature1, feature2`.
- `X-Fast-Or-Safe`: if set to `fast`, `blackd` will act as _Black_ does when passed the - `X-Fast-Or-Safe`: if set to `fast`, `blackd` will act as _Black_ does when passed the
`--fast` command line flag. `--fast` command line flag.
- `X-Python-Variant`: if set to `pyi`, `blackd` will act as _Black_ does when passed the - `X-Python-Variant`: if set to `pyi`, `blackd` will act as _Black_ does when passed the

View File

@ -1,53 +0,0 @@
# Black Docker image
Official _Black_ Docker images are available on
[Docker Hub](https://hub.docker.com/r/pyfound/black).
_Black_ images with the following tags are available:
- release numbers, e.g. `21.5b2`, `21.6b0`, `21.7b0` etc.\
Recommended for users who want to use a particular version of _Black_.
- `latest_release` - tag created when a new version of _Black_ is released.\
Recommended for users who want to use released versions of _Black_. It maps to
[the latest release](https://github.com/psf/black/releases/latest) of _Black_.
- `latest_prerelease` - tag created when a new alpha (prerelease) version of _Black_ is
released.\
Recommended for users who want to preview or test alpha versions of _Black_. Note
that the most recent release may be newer than any prerelease, because no prereleases
are created before most releases.
- `latest` - tag used for the newest image of _Black_.\
Recommended for users who always want to use the latest version of _Black_, even
before it is released.
There is one more tag used for _Black_ Docker images - `latest_non_release`. It is
created for all unreleased
[commits on the `main` branch](https://github.com/psf/black/commits/main). This tag is
not meant to be used by external users.
From version 23.11.0 the Docker image installs a compiled black into the image.
## Usage
A permanent container doesn't have to be created to use _Black_ as a Docker image. It's
enough to run _Black_ commands for the chosen image denoted as `:tag`. In the below
examples, the `latest_release` tag is used. If `:tag` is omitted, the `latest` tag will
be used.
More about _Black_ usage can be found in
[Usage and Configuration: The basics](./the_basics.md).
### Check Black version
```console
$ docker run --rm pyfound/black:latest_release black --version
```
### Check code
```console
$ docker run --rm --volume $(pwd):/src --workdir /src pyfound/black:latest_release black --check .
```
_Remark_: besides [regular _Black_ exit codes](./the_basics.md) returned by `--check`
option, [Docker exit codes](https://docs.docker.com/engine/reference/run/#exit-status)
should also be considered.

View File

@ -22,12 +22,10 @@ run. The file is non-portable. The standard location on common operating systems
`file-mode` is an int flag that determines whether the file was formatted as 3.6+ only, `file-mode` is an int flag that determines whether the file was formatted as 3.6+ only,
as .pyi, and whether string normalization was omitted. as .pyi, and whether string normalization was omitted.
To override the location of these files on all systems, set the environment variable To override the location of these files on macOS or Linux, set the environment variable
`BLACK_CACHE_DIR` to the preferred location. Alternatively on macOS and Linux, set
`XDG_CACHE_HOME` to your preferred location. For example, if you want to put the cache `XDG_CACHE_HOME` to your preferred location. For example, if you want to put the cache
in the directory you're running _Black_ from, set `BLACK_CACHE_DIR=.cache/black`. in the directory you're running _Black_ from, set `XDG_CACHE_HOME=.cache`. _Black_ will
_Black_ will then write the above files to `.cache/black`. Note that `BLACK_CACHE_DIR` then write the above files to `.cache/black/<version>/`.
will take precedence over `XDG_CACHE_HOME` if both are set.
## .gitignore ## .gitignore

View File

@ -1,28 +0,0 @@
# Usage and Configuration
```{toctree}
---
hidden:
---
the_basics
file_collection_and_discovery
black_as_a_server
black_docker_image
```
Sometimes, running _Black_ with its defaults and passing filepaths to it just won't cut
it. Passing each file using paths will become burdensome, and maybe you would like
_Black_ to not touch your files and just output diffs. And yes, you _can_ tweak certain
parts of _Black_'s style, but please know that configurability in this area is
purposefully limited.
Using many of these more advanced features of _Black_ will require some configuration.
Configuration that will either live on the command line or in a TOML configuration file.
This section covers features of _Black_ and configuring _Black_ in detail:
- {doc}`The basics <./the_basics>`
- {doc}`File collection and discovery <file_collection_and_discovery>`
- {doc}`Black as a server (blackd) <./black_as_a_server>`
- {doc}`Black Docker image <./black_docker_image>`

View File

@ -0,0 +1,24 @@
Usage and Configuration
=======================
.. toctree::
:hidden:
the_basics
file_collection_and_discovery
black_as_a_server
Sometimes, running *Black* with its defaults and passing filepaths to it just won't cut
it. Passing each file using paths will become burdensome, and maybe you would like
*Black* to not touch your files and just output diffs. And yes, you *can* tweak certain
parts of *Black*'s style, but please know that configurability in this area is
purposefully limited.
Using many of these more advanced features of *Black* will require some configuration.
Configuration that will either live on the command line or in a TOML configuration file.
This section covers features of *Black* and configuring *Black* in detail:
- :doc:`The basics <./the_basics>`
- :doc:`File collection and discovery <file_collection_and_discovery>`
- :doc:`Black as a server (blackd) <./black_as_a_server>`

View File

@ -4,16 +4,15 @@ Foundational knowledge on using and configuring Black.
_Black_ is a well-behaved Unix-style command-line tool: _Black_ is a well-behaved Unix-style command-line tool:
- it does nothing if it finds no sources to format; - it does nothing if no sources are passed to it;
- it will read from standard input and write to standard output if `-` is used as the - it will read from standard input and write to standard output if `-` is used as the
filename; filename;
- it only outputs messages to users on standard error; - it only outputs messages to users on standard error;
- exits with code 0 unless an internal error occurred or a CLI option prompted it. - exits with code 0 unless an internal error occurred (or `--check` was used).
## Usage ## Usage
_Black_ will reformat entire files in place. To get started right away with sensible To get started right away with sensible defaults:
defaults:
```sh ```sh
black {source_file_or_directory} black {source_file_or_directory}
@ -25,167 +24,66 @@ You can run _Black_ as a package if running it as a script doesn't work:
python -m black {source_file_or_directory} python -m black {source_file_or_directory}
``` ```
### Ignoring sections
Black will not reformat lines that contain `# fmt: skip` or blocks that start with
`# fmt: off` and end with `# fmt: on`. `# fmt: skip` can be mixed with other
pragmas/comments either with multiple comments (e.g. `# fmt: skip # pylint # noqa`) or
as a semicolon separated list (e.g. `# fmt: skip; pylint; noqa`). `# fmt: on/off` must
be on the same level of indentation and in the same block, meaning no unindents beyond
the initial indentation level between them. Black also recognizes
[YAPF](https://github.com/google/yapf)'s block comments to the same effect, as a
courtesy for straddling code.
### Command line options ### Command line options
The CLI options of _Black_ can be displayed by running `black --help`. All options are _Black_ has quite a few knobs these days, although _Black_ is opinionated so style
also covered in more detail below. configuration options are deliberately limited and rarely added. You can list them by
running `black --help`.
While _Black_ has quite a few knobs these days, it is still opinionated so style options <details>
are deliberately limited and rarely added.
Note that all command-line options listed above can also be configured using a <summary>Help output</summary>
`pyproject.toml` file (more on that below).
#### `-h`, `--help` ```{program-output} black --help
Show available command-line options and exit. ```
#### `-c`, `--code` </details>
Format the code passed in as a string. ### Code input alternatives
#### Standard Input
_Black_ supports formatting code via stdin, with the result being printed to stdout.
Just let _Black_ know with `-` as the path.
```console
$ echo "print ( 'hello, world' )" | black -
print("hello, world")
reformatted -
All done! ✨ 🍰 ✨
1 file reformatted.
```
**Tip:** if you need _Black_ to treat stdin input as a file passed directly via the CLI,
use `--stdin-filename`. Useful to make sure _Black_ will respect the `--force-exclude`
option on some editors that rely on using stdin.
#### As a string
You can also pass code as a string using the `-c` / `--code` option.
```console ```console
$ black --code "print ( 'hello, world' )" $ black --code "print ( 'hello, world' )"
print("hello, world") print("hello, world")
``` ```
#### `-l`, `--line-length` ### Writeback and reporting
How many characters per line to allow. The default is 88. By default _Black_ reformats the files given and/or found in place. Sometimes you need
_Black_ to just tell you what it _would_ do without actually rewriting the Python files.
See also [the style documentation](labels/line-length). There's two variations to this mode that are independently enabled by their respective
flags. Both variations can be enabled at once.
#### `-t`, `--target-version` #### Exit code
Python versions that should be supported by Black's output. You can run `black --help` Passing `--check` will make _Black_ exit with:
and look for the `--target-version` option to see the full list of supported versions.
You should include all versions that your code supports. If you support Python 3.11
through 3.13, you should write:
```console
$ black -t py311 -t py312 -t py313
```
In a [configuration file](#configuration-via-a-file), you can write:
```toml
target-version = ["py311", "py312", "py313"]
```
By default, Black will infer target versions from the project metadata in
`pyproject.toml`, specifically the `[project.requires-python]` field. If this does not
yield conclusive results, Black will use per-file auto-detection.
_Black_ uses this option to decide what grammar to use to parse your code. In addition,
it may use it to decide what style to use. For example, support for a trailing comma
after `*args` in a function call was added in Python 3.5, so _Black_ will add this comma
only if the target versions are all Python 3.5 or higher:
```console
$ black --line-length=10 --target-version=py35 -c 'f(a, *args)'
f(
a,
*args,
)
$ black --line-length=10 --target-version=py34 -c 'f(a, *args)'
f(
a,
*args
)
$ black --line-length=10 --target-version=py34 --target-version=py35 -c 'f(a, *args)'
f(
a,
*args
)
```
#### `--pyi`
Format all input files like typing stubs regardless of file extension. This is useful
when piping source on standard input.
#### `--ipynb`
Format all input files like Jupyter Notebooks regardless of file extension. This is
useful when piping source on standard input.
#### `--python-cell-magics`
When processing Jupyter Notebooks, add the given magic to the list of known python-
magics. Useful for formatting cells with custom python magics.
#### `-x, --skip-source-first-line`
Skip the first line of the source code.
#### `-S, --skip-string-normalization`
By default, _Black_ uses double quotes for all strings and normalizes string prefixes,
as described in [the style documentation](labels/strings). If this option is given,
strings are left unchanged instead.
#### `-C, --skip-magic-trailing-comma`
By default, _Black_ uses existing trailing commas as an indication that short lines
should be left separate, as described in
[the style documentation](labels/magic-trailing-comma). If this option is given, the
magic trailing comma is ignored.
#### `--preview`
Enable potentially disruptive style changes that we expect to add to Black's main
functionality in the next major release. Use this if you want a taste of what next
year's style will look like.
Read more about [our preview style](labels/preview-style).
There is no guarantee on the code style produced by this flag across releases.
#### `--unstable`
Enable all style changes in `--preview`, plus additional changes that we would like to
make eventually, but that have known issues that need to be fixed before they can move
back to the `--preview` style. Use this if you want to experiment with these changes and
help fix issues with them.
There is no guarantee on the code style produced by this flag across releases.
#### `--enable-unstable-feature`
Enable specific features from the `--unstable` style. See
[the preview style documentation](labels/unstable-features) for the list of supported
features. This flag can only be used when `--preview` is enabled. Users are encouraged
to use this flag if they use `--preview` style and a feature that affects their code is
moved from the `--preview` to the `--unstable` style, but they want to avoid the thrash
from undoing this change.
There are no guarantees on the behavior of these features, or even their existence,
across releases.
(labels/exit-code)=
#### `--check`
Don't write the files back, just return the status. _Black_ will exit with:
- code 0 if nothing would change; - code 0 if nothing would change;
- code 1 if some files would be reformatted; or - code 1 if some files would be reformatted; or
- code 123 if there was an internal error - code 123 if there was an internal error
If used in combination with `--quiet` then only the exit code will be returned, unless
there was an internal error.
```console ```console
$ black test.py --check $ black test.py --check
All done! ✨ 🍰 ✨ All done! ✨ 🍰 ✨
@ -208,17 +106,17 @@ $ echo $?
123 123
``` ```
#### `--diff` #### Diffs
Don't write the files back, just output a diff to indicate what changes _Black_ would've Passing `--diff` will make _Black_ print out diffs that indicate what changes _Black_
made. They are printed to stdout so capturing them is simple. would've made. They are printed to stdout so capturing them is simple.
If you'd like colored diffs, you can enable them with `--color`. If you'd like colored diffs, you can enable them with the `--color`.
```console ```console
$ black test.py --diff $ black test.py --diff
--- test.py 2021-03-08 22:23:40.848954+00:00 --- test.py 2021-03-08 22:23:40.848954 +0000
+++ test.py 2021-03-08 22:23:47.126319+00:00 +++ test.py 2021-03-08 22:23:47.126319 +0000
@@ -1 +1 @@ @@ -1 +1 @@
-print ( 'hello, world' ) -print ( 'hello, world' )
+print("hello, world") +print("hello, world")
@ -227,198 +125,6 @@ All done! ✨ 🍰 ✨
1 file would be reformatted. 1 file would be reformatted.
``` ```
#### `--color` / `--no-color`
Show (or do not show) colored diff. Only applies when `--diff` is given.
#### `--line-ranges`
When specified, _Black_ will try its best to only format these lines.
This option can be specified multiple times, and a union of the lines will be formatted.
Each range must be specified as two integers connected by a `-`: `<START>-<END>`. The
`<START>` and `<END>` integer indices are 1-based and inclusive on both ends.
_Black_ may still format lines outside of the ranges for multi-line statements.
Formatting more than one file or any ipynb files with this option is not supported. This
option cannot be specified in the `pyproject.toml` config.
Example: `black --line-ranges=1-10 --line-ranges=21-30 test.py` will format lines from
`1` to `10` and `21` to `30`.
This option is mainly for editor integrations, such as "Format Selection".
```{note}
Due to [#4052](https://github.com/psf/black/issues/4052), `--line-ranges` might format
extra lines outside of the ranges when ther are unformatted lines with the exact
content. It also disables _Black_'s formatting stability check in `--safe` mode.
```
#### `--fast` / `--safe`
By default, _Black_ performs [an AST safety check](labels/ast-changes) after formatting
your code. The `--fast` flag turns off this check and the `--safe` flag explicitly
enables it.
#### `--required-version`
Require a specific version of _Black_ to be running. This is useful for ensuring that
all contributors to your project are using the same version, because different versions
of _Black_ may format code a little differently. This option can be set in a
configuration file for consistent results across environments.
```console
$ black --version
black, 25.1.0 (compiled: yes)
$ black --required-version 25.1.0 -c "format = 'this'"
format = "this"
$ black --required-version 31.5b2 -c "still = 'beta?!'"
Oh no! 💥 💔 💥 The required version does not match the running version!
```
You can also pass just the major version:
```console
$ black --required-version 22 -c "format = 'this'"
format = "this"
$ black --required-version 31 -c "still = 'beta?!'"
Oh no! 💥 💔 💥 The required version does not match the running version!
```
Because of our [stability policy](../the_black_code_style/index.md), this will guarantee
stable formatting, but still allow you to take advantage of improvements that do not
affect formatting.
#### `--exclude`
A regular expression that matches files and directories that should be excluded on
recursive searches. An empty value means no paths are excluded. Use forward slashes for
directories on all platforms (Windows, too). By default, Black also ignores all paths
listed in `.gitignore`. Changing this value will override all default exclusions.
If the regular expression contains newlines, it is treated as a
[verbose regular expression](https://docs.python.org/3/library/re.html#re.VERBOSE). This
is typically useful when setting these options in a `pyproject.toml` configuration file;
see [Configuration format](#configuration-format) for more information.
#### `--extend-exclude`
Like `--exclude`, but adds additional files and directories on top of the default values
instead of overriding them.
#### `--force-exclude`
Like `--exclude`, but files and directories matching this regex will be excluded even
when they are passed explicitly as arguments. This is useful when invoking Black
programmatically on changed files, such as in a pre-commit hook or editor plugin.
#### `--stdin-filename`
The name of the file when passing it through stdin. Useful to make sure Black will
respect the `--force-exclude` option on some editors that rely on using stdin.
#### `--include`
A regular expression that matches files and directories that should be included on
recursive searches. An empty value means all files are included regardless of the name.
Use forward slashes for directories on all platforms (Windows, too). Overrides all
exclusions, including from `.gitignore` and command line options.
#### `-W`, `--workers`
When _Black_ formats multiple files, it may use a process pool to speed up formatting.
This option controls the number of parallel workers. This can also be specified via the
`BLACK_NUM_WORKERS` environment variable. Defaults to the number of CPUs in the system.
#### `-q`, `--quiet`
Stop emitting all non-critical output. Error messages will still be emitted (which can
silenced by `2>/dev/null`).
```console
$ black src/ -q
error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio
```
#### `-v`, `--verbose`
Emit messages about files that were not changed or were ignored due to exclusion
patterns. If _Black_ is using a configuration file, a message detailing which one it is
using will be emitted.
```console
$ black src/ -v
Using configuration from /tmp/pyproject.toml.
src/blib2to3 ignored: matches the --extend-exclude regular expression
src/_black_version.py wasn't modified on disk since last run.
src/black/__main__.py wasn't modified on disk since last run.
error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio
reformatted src/black_primer/lib.py
reformatted src/blackd/__init__.py
reformatted src/black/__init__.py
Oh no! 💥 💔 💥
3 files reformatted, 2 files left unchanged, 1 file failed to reformat
```
#### `--version`
You can check the version of _Black_ you have installed using the `--version` flag.
```console
$ black --version
black, 25.1.0
```
#### `--config`
Read configuration options from a configuration file. See
[below](#configuration-via-a-file) for more details on the configuration file.
### Environment variable options
_Black_ supports the following configuration via environment variables.
#### `BLACK_CACHE_DIR`
The directory where _Black_ should store its cache.
#### `BLACK_NUM_WORKERS`
The number of parallel workers _Black_ should use. The command line option `-W` /
`--workers` takes precedence over this environment variable.
### Code input alternatives
_Black_ supports formatting code via stdin, with the result being printed to stdout.
Just let _Black_ know with `-` as the path.
```console
$ echo "print ( 'hello, world' )" | black -
print("hello, world")
reformatted -
All done! ✨ 🍰 ✨
1 file reformatted.
```
**Tip:** if you need _Black_ to treat stdin input as a file passed directly via the CLI,
use `--stdin-filename`. Useful to make sure _Black_ will respect the `--force-exclude`
option on some editors that rely on using stdin.
You can also pass code as a string using the `--code` option.
### Writeback and reporting
By default _Black_ reformats the files given and/or found in place. Sometimes you need
_Black_ to just tell you what it _would_ do without actually rewriting the Python files.
There's two variations to this mode that are independently enabled by their respective
flags:
- `--check` (exit with code 1 if any file would be reformatted)
- `--diff` (print a diff instead of reformatting files)
Both variations can be enabled at once.
### Output verbosity ### Output verbosity
_Black_ in general tries to produce the right amount of output, balancing between _Black_ in general tries to produce the right amount of output, balancing between
@ -435,7 +141,53 @@ Oh no! 💥 💔 💥
3 files reformatted, 2 files left unchanged, 1 file failed to reformat. 3 files reformatted, 2 files left unchanged, 1 file failed to reformat.
``` ```
The `--quiet` and `--verbose` flags control output verbosity. Passing `-v` / `--verbose` will cause _Black_ to also emit messages about files that
were not changed or were ignored due to exclusion patterns. If _Black_ is using a
configuration file, a blue message detailing which one it is using will be emitted.
```console
$ black src/ -v
Using configuration from /tmp/pyproject.toml.
src/blib2to3 ignored: matches the --extend-exclude regular expression
src/_black_version.py wasn't modified on disk since last run.
src/black/__main__.py wasn't modified on disk since last run.
error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio
reformatted src/black_primer/lib.py
reformatted src/blackd/__init__.py
reformatted src/black/__init__.py
Oh no! 💥 💔 💥
3 files reformatted, 2 files left unchanged, 1 file failed to reformat
```
Passing `-q` / `--quiet` will cause _Black_ to stop emitting all non-critial output.
Error messages will still be emitted (which can silenced by `2>/dev/null`).
```console
$ black src/ -q
error: cannot format src/black_primer/cli.py: Cannot parse: 5:6: mport asyncio
```
### Versions
You can check the version of _Black_ you have installed using the `--version` flag.
```console
$ black --version
black, version 21.8b0
```
An option to require a specific version to be running is also provided.
```console
$ black --required-version 21.8b0 -c "format = 'this'"
format = "this"
$ black --required-version 31.5b2 -c "still = 'beta?!'"
Oh no! 💥 💔 💥 The required version does not match the running version!
```
This is useful for example when running _Black_ in multiple environments that haven't
necessarily installed the correct version. This option can be set in a configuration
file for consistent results across environments.
## Configuration via a file ## Configuration via a file
@ -452,18 +204,16 @@ code in compliance with many other _Black_ formatted projects.
[PEP 518](https://www.python.org/dev/peps/pep-0518/) defines `pyproject.toml` as a [PEP 518](https://www.python.org/dev/peps/pep-0518/) defines `pyproject.toml` as a
configuration file to store build system requirements for Python projects. With the help configuration file to store build system requirements for Python projects. With the help
of tools like [Poetry](https://python-poetry.org/), of tools like [Poetry](https://python-poetry.org/) or
[Flit](https://flit.readthedocs.io/en/latest/), or [Flit](https://flit.readthedocs.io/en/latest/) it can fully replace the need for
[Hatch](https://hatch.pypa.io/latest/) it can fully replace the need for `setup.py` and `setup.py` and `setup.cfg` files.
`setup.cfg` files.
### Where _Black_ looks for the file ### Where _Black_ looks for the file
By default _Black_ looks for `pyproject.toml` containing a `[tool.black]` section By default _Black_ looks for `pyproject.toml` starting from the common base directory of
starting from the common base directory of all files and directories passed on the all files and directories passed on the command line. If it's not there, it looks in
command line. If it's not there, it looks in parent directories. It stops looking when parent directories. It stops looking when it finds the file, or a `.git` directory, or a
it finds the file, or a `.git` directory, or a `.hg` directory, or the root of the file `.hg` directory, or the root of the file system, whichever comes first.
system, whichever comes first.
If you're formatting standard input, _Black_ will look for configuration starting from If you're formatting standard input, _Black_ will look for configuration starting from
the current working directory. the current working directory.
@ -478,15 +228,15 @@ operating system, this configuration file should be stored as:
`XDG_CONFIG_HOME` environment variable is not set) `XDG_CONFIG_HOME` environment variable is not set)
Note that these are paths to the TOML file itself (meaning that they shouldn't be named Note that these are paths to the TOML file itself (meaning that they shouldn't be named
as `pyproject.toml`), not directories where you store the configuration (i.e., as `pyproject.toml`), not directories where you store the configuration. Here, `~`
`black`/`.black` is the file to create and add your configuration options to, in the refers to the path to your home directory. On Windows, this will be something like
`~/.config/` directory). Here, `~` refers to the path to your home directory. On `C:\\Users\UserName`.
Windows, this will be something like `C:\\Users\UserName`.
You can also explicitly specify the path to a particular file that you want with You can also explicitly specify the path to a particular file that you want with
`--config`. In this situation _Black_ will not look for any other file. `--config`. In this situation _Black_ will not look for any other file.
If you're running with `--verbose`, you will see a message if a file was found and used. If you're running with `--verbose`, you will see a blue message if a file was found and
used.
Please note `blackd` will not use `pyproject.toml` configuration. Please note `blackd` will not use `pyproject.toml` configuration.
@ -509,14 +259,10 @@ expressions by Black. Use `[ ]` to denote a significant space character.
line-length = 88 line-length = 88
target-version = ['py37'] target-version = ['py37']
include = '\.pyi?$' include = '\.pyi?$'
# 'extend-exclude' excludes files or directories in addition to the defaults
extend-exclude = ''' extend-exclude = '''
# A regex preceded with ^/ will apply only to files and directories # A regex preceded with ^/ will apply only to files and directories
# in the root of the project. # 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)
^/foo.py # exclude a file named foo.py in the root of the project
| .*_pb2.py # exclude autogenerated Protocol Buffer files anywhere in the project
)
''' '''
``` ```
@ -534,6 +280,9 @@ file hierarchy.
## Next steps ## Next steps
You've probably noted that not all of the options you can pass to _Black_ have been
covered. Don't worry, the rest will be covered in a later section.
A good next step would be configuring auto-discovery so `black .` is all you need A good next step would be configuring auto-discovery so `black .` is all you need
instead of laborously listing every file or directory. You can get started by heading instead of laborously listing every file or directory. You can get started by heading
over to [File collection and discovery](./file_collection_and_discovery.md). over to [File collection and discovery](./file_collection_and_discovery.md).

View File

@ -5,11 +5,13 @@
a coverage-guided fuzzer I'm working on. a coverage-guided fuzzer I'm working on.
""" """
import re
import hypothesmith import hypothesmith
from hypothesis import HealthCheck, given, settings from hypothesis import HealthCheck, given, settings, strategies as st
from hypothesis import strategies as st
import black import black
from blib2to3.pgen2.tokenize import TokenError
# This test uses the Hypothesis and Hypothesmith libraries to generate random # This test uses the Hypothesis and Hypothesmith libraries to generate random
@ -18,7 +20,7 @@
max_examples=1000, # roughly 1k tests/minute, or half that under coverage max_examples=1000, # roughly 1k tests/minute, or half that under coverage
derandomize=True, # deterministic mode to avoid CI flakiness derandomize=True, # deterministic mode to avoid CI flakiness
deadline=None, # ignore Hypothesis' health checks; we already know that deadline=None, # ignore Hypothesis' health checks; we already know that
suppress_health_check=list(HealthCheck), # this is slow and filter-heavy. suppress_health_check=HealthCheck.all(), # this is slow and filter-heavy.
) )
@given( @given(
# Note that while Hypothesmith might generate code unlike that written by # Note that while Hypothesmith might generate code unlike that written by
@ -30,9 +32,7 @@
black.FileMode, black.FileMode,
line_length=st.just(88) | st.integers(0, 200), line_length=st.just(88) | st.integers(0, 200),
string_normalization=st.booleans(), string_normalization=st.booleans(),
preview=st.booleans(),
is_pyi=st.booleans(), is_pyi=st.booleans(),
magic_trailing_comma=st.booleans(),
), ),
) )
def test_idempotent_any_syntatically_valid_python( def test_idempotent_any_syntatically_valid_python(
@ -42,7 +42,23 @@ def test_idempotent_any_syntatically_valid_python(
compile(src_contents, "<string>", "exec") # else the bug is in hypothesmith compile(src_contents, "<string>", "exec") # else the bug is in hypothesmith
# Then format the code... # Then format the code...
try:
dst_contents = black.format_str(src_contents, mode=mode) dst_contents = black.format_str(src_contents, mode=mode)
except black.InvalidInput:
# This is a bug - if it's valid Python code, as above, Black should be
# able to cope with it. See issues #970, #1012, #1358, and #1557.
# TODO: remove this try-except block when issues are resolved.
return
except TokenError as e:
if ( # Special-case logic for backslashes followed by newlines or end-of-input
e.args[0] == "EOF in multi-line statement"
and re.search(r"\\($|\r?\n)", src_contents) is not None
):
# This is a bug - if it's valid Python code, as above, Black should be
# able to cope with it. See issue #1012.
# TODO: remove this block when the issue is resolved.
return
raise
# And check that we got equivalent and stable output. # And check that we got equivalent and stable output.
black.assert_equivalent(src_contents, dst_contents) black.assert_equivalent(src_contents, dst_contents)
@ -60,14 +76,10 @@ def test_idempotent_any_syntatically_valid_python(
# (if you want only bounded fuzzing, just use `pytest fuzz.py`) # (if you want only bounded fuzzing, just use `pytest fuzz.py`)
try: try:
import sys import sys
import atheris import atheris
except ImportError: except ImportError:
pass pass
else: else:
test = test_idempotent_any_syntatically_valid_python test = test_idempotent_any_syntatically_valid_python
atheris.Setup( atheris.Setup(sys.argv, test.hypothesis.fuzz_one_input)
sys.argv,
test.hypothesis.fuzz_one_input, # type: ignore[attr-defined]
)
atheris.Fuzz() atheris.Fuzz()

View File

@ -7,20 +7,29 @@
import venv import venv
import zipfile import zipfile
from argparse import ArgumentParser, Namespace from argparse import ArgumentParser, Namespace
from collections.abc import Generator
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from functools import lru_cache, partial from functools import lru_cache, partial
from pathlib import Path from pathlib import Path
from typing import NamedTuple, Optional, Union, cast from typing import ( # type: ignore # typing can't see Literal
Generator,
List,
Literal,
NamedTuple,
Optional,
Tuple,
Union,
cast,
)
from urllib.request import urlopen, urlretrieve from urllib.request import urlopen, urlretrieve
PYPI_INSTANCE = "https://pypi.org/pypi" PYPI_INSTANCE = "https://pypi.org/pypi"
PYPI_TOP_PACKAGES = ( PYPI_TOP_PACKAGES = (
"https://hugovk.github.io/top-pypi-packages/top-pypi-packages.min.json" "https://hugovk.github.io/top-pypi-packages/top-pypi-packages-{days}-days.json"
) )
INTERNAL_BLACK_REPO = f"{tempfile.gettempdir()}/__black" INTERNAL_BLACK_REPO = f"{tempfile.gettempdir()}/__black"
ArchiveKind = Union[tarfile.TarFile, zipfile.ZipFile] ArchiveKind = Union[tarfile.TarFile, zipfile.ZipFile]
Days = Union[Literal[30], Literal[365]]
subprocess.run = partial(subprocess.run, check=True) # type: ignore subprocess.run = partial(subprocess.run, check=True) # type: ignore
# https://github.com/python/mypy/issues/1484 # https://github.com/python/mypy/issues/1484
@ -55,8 +64,8 @@ def get_pypi_download_url(package: str, version: Optional[str]) -> str:
return cast(str, source["url"]) return cast(str, source["url"])
def get_top_packages() -> list[str]: def get_top_packages(days: Days) -> List[str]:
with urlopen(PYPI_TOP_PACKAGES) as page: with urlopen(PYPI_TOP_PACKAGES.format(days=days)) as page:
result = json.load(page) result = json.load(page)
return [package["project"] for package in result["rows"]] return [package["project"] for package in result["rows"]]
@ -119,12 +128,13 @@ def get_package(
def download_and_extract_top_packages( def download_and_extract_top_packages(
directory: Path, directory: Path,
days: Days = 365,
workers: int = 8, workers: int = 8,
limit: slice = DEFAULT_SLICE, limit: slice = DEFAULT_SLICE,
) -> Generator[Path, None, None]: ) -> Generator[Path, None, None]:
with ThreadPoolExecutor(max_workers=workers) as executor: with ThreadPoolExecutor(max_workers=workers) as executor:
bound_downloader = partial(get_package, version=None, directory=directory) bound_downloader = partial(get_package, version=None, directory=directory)
for package in executor.map(bound_downloader, get_top_packages()[limit]): for package in executor.map(bound_downloader, get_top_packages(days)[limit]):
if package is not None: if package is not None:
yield package yield package
@ -151,7 +161,7 @@ def git_switch_branch(
subprocess.run(args, cwd=repo) subprocess.run(args, cwd=repo)
def init_repos(options: Namespace) -> tuple[Path, ...]: def init_repos(options: Namespace) -> Tuple[Path, ...]:
options.output.mkdir(exist_ok=True) options.output.mkdir(exist_ok=True)
if options.top_packages: if options.top_packages:
@ -207,7 +217,7 @@ def format_repo_with_version(
git_switch_branch(black_version.version, repo=black_repo) git_switch_branch(black_version.version, repo=black_repo)
git_switch_branch(current_branch, repo=repo, new=True, from_branch=from_branch) git_switch_branch(current_branch, repo=repo, new=True, from_branch=from_branch)
format_cmd: list[Union[Path, str]] = [ format_cmd: List[Union[Path, str]] = [
black_runner(black_version.version, black_repo), black_runner(black_version.version, black_repo),
(black_repo / "black.py").resolve(), (black_repo / "black.py").resolve(),
".", ".",
@ -223,7 +233,7 @@ def format_repo_with_version(
return current_branch return current_branch
def format_repos(repos: tuple[Path, ...], options: Namespace) -> None: def format_repos(repos: Tuple[Path, ...], options: Namespace) -> None:
black_versions = tuple( black_versions = tuple(
BlackVersion(*version.split(":")) for version in options.versions BlackVersion(*version.split(":")) for version in options.versions
) )
@ -244,9 +254,11 @@ def format_repos(repos: tuple[Path, ...], options: Namespace) -> None:
def main() -> None: def main() -> None:
parser = ArgumentParser(description="""Black Gallery is a script that parser = ArgumentParser(
description="""Black Gallery is a script that
automates the process of applying different Black versions to a selected automates the process of applying different Black versions to a selected
PyPI package and seeing the results between versions.""") PyPI package and seeing the results between versions."""
)
group = parser.add_mutually_exclusive_group(required=True) group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("-p", "--pypi-package", help="PyPI package to download.") group.add_argument("-p", "--pypi-package", help="PyPI package to download.")

41
mypy.ini Normal file
View File

@ -0,0 +1,41 @@
[mypy]
# Specify the target platform details in config, so your developers are
# free to run mypy on Windows, Linux, or macOS and get consistent
# results.
python_version=3.6
platform=linux
mypy_path=src
show_column_numbers=True
# show error messages from unrelated files
follow_imports=normal
# suppress errors about unsatisfied imports
ignore_missing_imports=True
# be strict
disallow_untyped_calls=True
warn_return_any=True
strict_optional=True
warn_no_return=True
warn_redundant_casts=True
warn_unused_ignores=True
# Until we're not supporting 3.6 primer needs this
disallow_any_generics=False
# The following are off by default. Flip them on if you feel
# adventurous.
disallow_untyped_defs=True
check_untyped_defs=True
# No incremental mode
cache_dir=/dev/null
[mypy-aiohttp.*]
follow_imports=skip
[mypy-black]
# The following is because of `patch_click()`. Remove when
# we drop Python 3.6 support.
warn_unused_ignores=False

View File

@ -15,13 +15,9 @@
" 1.2: " 1.2:
" - use autoload script " - use autoload script
if exists("g:load_black")
finish
endif
if v:version < 700 || !has('python3') if v:version < 700 || !has('python3')
func! __BLACK_MISSING() func! __BLACK_MISSING()
echo "The black.vim plugin requires vim7.0+ with Python 3.9 support." echo "The black.vim plugin requires vim7.0+ with Python 3.6 support."
endfunc endfunc
command! Black :call __BLACK_MISSING() command! Black :call __BLACK_MISSING()
command! BlackUpgrade :call __BLACK_MISSING() command! BlackUpgrade :call __BLACK_MISSING()
@ -29,6 +25,10 @@ if v:version < 700 || !has('python3')
finish finish
endif endif
if exists("g:load_black")
finish
endif
let g:load_black = "py1.0" let g:load_black = "py1.0"
if !exists("g:black_virtualenv") if !exists("g:black_virtualenv")
if has("nvim") if has("nvim")
@ -50,36 +50,11 @@ if !exists("g:black_skip_string_normalization")
let g:black_skip_string_normalization = 0 let g:black_skip_string_normalization = 0
endif endif
endif endif
if !exists("g:black_skip_magic_trailing_comma")
if exists("g:black_magic_trailing_comma")
let g:black_skip_magic_trailing_comma = !g:black_magic_trailing_comma
else
let g:black_skip_magic_trailing_comma = 0
endif
endif
if !exists("g:black_quiet") if !exists("g:black_quiet")
let g:black_quiet = 0 let g:black_quiet = 0
endif endif
if !exists("g:black_target_version")
let g:black_target_version = ""
endif
if !exists("g:black_use_virtualenv")
let g:black_use_virtualenv = 1
endif
if !exists("g:black_preview")
let g:black_preview = 0
endif
function BlackComplete(ArgLead, CmdLine, CursorPos)
return [
\ 'target_version=py39',
\ 'target_version=py310',
\ 'target_version=py311',
\ 'target_version=py312',
\ 'target_version=py313',
\ ]
endfunction
command! -nargs=* -complete=customlist,BlackComplete Black :call black#Black(<f-args>) command! Black :call black#Black()
command! BlackUpgrade :call black#BlackUpgrade() command! BlackUpgrade :call black#BlackUpgrade()
command! BlackVersion :call black#BlackVersion() command! BlackVersion :call black#BlackVersion()

View File

@ -7,238 +7,32 @@
[tool.black] [tool.black]
line-length = 88 line-length = 88
target-version = ['py39'] target-version = ['py36', 'py37', 'py38']
include = '\.pyi?$' include = '\.pyi?$'
extend-exclude = ''' extend-exclude = '''
/( /(
# The following are specific to Black, you probably don't want those. # The following are specific to Black, you probably don't want those.
tests/data/ | blib2to3
| profiling/ | tests/data
| scripts/generate_schema.py # Uses match syntax | profiling
) )/
''' '''
# We use the unstable style for formatting Black itself. If you
# want bug-free formatting, you should keep this off. If you want
# stable formatting across releases, you should also keep `preview = true`
# (which is implied by this flag) off.
unstable = true
# Build system information and other project-specific configuration below.
# Build system information below.
# NOTE: You don't need this in your own Black configuration. # NOTE: You don't need this in your own Black configuration.
[build-system] [build-system]
requires = ["hatchling>=1.20.0", "hatch-vcs", "hatch-fancy-pypi-readme"] # We're pinning setuptools-scm to bugfix versions only because for build-time
build-backend = "hatchling.build" # deps having them work on install by default is really important. Especially
# since it's hard for users to work-around the specified build requirements.
[project] requires = ["setuptools>=41.0", "setuptools_scm~=6.0.1", "wheel"]
name = "black" build-backend = "setuptools.build_meta"
description = "The uncompromising code formatter."
license = "MIT"
requires-python = ">=3.9"
authors = [
{ name = "Łukasz Langa", email = "lukasz@langa.pl" },
]
keywords = [
"automation",
"autopep8",
"formatter",
"gofmt",
"pyfmt",
"rustfmt",
"yapf",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
]
dependencies = [
"click>=8.0.0",
"mypy_extensions>=0.4.3",
"packaging>=22.0",
"pathspec>=0.9.0",
"platformdirs>=2",
"pytokens>=0.1.10",
"tomli>=1.1.0; python_version < '3.11'",
"typing_extensions>=4.0.1; python_version < '3.11'",
]
dynamic = ["readme", "version"]
[project.optional-dependencies]
colorama = ["colorama>=0.4.3"]
uvloop = ["uvloop>=0.15.2"]
d = ["aiohttp>=3.10"]
jupyter = [
"ipython>=7.8.0",
"tokenize-rt>=3.2.0",
]
[project.scripts]
black = "black:patched_main"
blackd = "blackd:patched_main [d]"
[project.entry-points."validate_pyproject.tool_schema"]
black = "black.schema:get_schema"
[project.urls]
Documentation = "https://black.readthedocs.io/"
Changelog = "https://github.com/psf/black/blob/main/CHANGES.md"
Repository = "https://github.com/psf/black"
Issues = "https://github.com/psf/black/issues"
[tool.hatch.metadata.hooks.fancy-pypi-readme]
content-type = "text/markdown"
fragments = [
{ path = "README.md" },
{ path = "CHANGES.md" },
]
[tool.hatch.version]
source = "vcs"
[tool.hatch.build.hooks.vcs]
version-file = "src/_black_version.py"
template = '''
version = "{version}"
'''
[tool.hatch.build.targets.sdist]
exclude = ["/profiling"]
[tool.hatch.build.targets.wheel]
only-include = ["src"]
sources = ["src"]
# Note that we change the behaviour of this flag below
macos-max-compat = true
[tool.hatch.build.targets.wheel.hooks.mypyc]
enable-by-default = false
dependencies = [
"hatch-mypyc>=0.16.0",
"mypy>=1.12",
"click>=8.1.7",
]
require-runtime-dependencies = true
exclude = [
# There's no good reason for blackd to be compiled.
"/src/blackd",
# Not performance sensitive, so save bytes + compilation time:
"/src/blib2to3/__init__.py",
"/src/blib2to3/pgen2/__init__.py",
"/src/black/output.py",
"/src/black/concurrency.py",
"/src/black/files.py",
"/src/black/report.py",
# Breaks the test suite when compiled (and is also useless):
"/src/black/debug.py",
# Compiled modules can't be run directly and that's a problem here:
"/src/black/__main__.py",
]
mypy-args = ["--ignore-missing-imports"]
options = { debug_level = "0" }
[tool.cibuildwheel]
build-verbosity = 1
# So these are the environments we target:
# - Python: CPython 3.9+ only
# - Architecture (64-bit only): amd64 / x86_64, universal2, and arm64
# - OS: Linux (no musl), Windows, and macOS
build = "cp3*"
skip = ["*-manylinux_i686", "*-musllinux_*", "*-win32", "pp*"]
# This is the bare minimum needed to run the test suite. Pulling in the full
# test_requirements.txt would download a bunch of other packages not necessary
# here and would slow down the testing step a fair bit.
test-requires = ["pytest>=6.1.1"]
test-command = 'pytest {project} -k "not incompatible_with_mypyc"'
test-extras = ["d"," jupyter"]
# Skip trying to test arm64 builds on Intel Macs. (so cross-compilation doesn't
# straight up crash)
test-skip = ["*-macosx_arm64", "*-macosx_universal2:arm64"]
[tool.cibuildwheel.environment]
HATCH_BUILD_HOOKS_ENABLE = "1"
MYPYC_OPT_LEVEL = "3"
MYPYC_DEBUG_LEVEL = "0"
[tool.cibuildwheel.linux]
manylinux-x86_64-image = "manylinux_2_28"
before-build = [
"yum install -y clang gcc",
]
[tool.cibuildwheel.linux.environment]
HATCH_BUILD_HOOKS_ENABLE = "1"
MYPYC_OPT_LEVEL = "3"
MYPYC_DEBUG_LEVEL = "0"
# Black needs Clang to compile successfully on Linux.
CC = "clang"
[tool.isort]
atomic = true
profile = "black"
line_length = 88
skip_gitignore = true
skip_glob = ["tests/data", "profiling"]
known_first_party = ["black", "blib2to3", "blackd", "_black_version"]
[tool.pytest.ini_options] [tool.pytest.ini_options]
# Option below requires `tests/optional.py` # Option below requires `tests/optional.py`
addopts = "--strict-config --strict-markers"
optional-tests = [ optional-tests = [
"no_python2: run when `python2` extra NOT installed",
"no_blackd: run when `d` extra NOT installed", "no_blackd: run when `d` extra NOT installed",
"no_jupyter: run when `jupyter` extra NOT installed", "no_jupyter: run when `jupyter` extra NOT installed",
] ]
markers = [
"incompatible_with_mypyc: run when testing mypyc compiled black"
]
xfail_strict = true
filterwarnings = ["error"]
[tool.coverage.report]
omit = [
"src/blib2to3/*",
"tests/data/*",
"*/site-packages/*",
".tox/*"
]
[tool.coverage.run]
relative_files = true
branch = true
[tool.mypy]
# Specify the target platform details in config, so your developers are
# free to run mypy on Windows, Linux, or macOS and get consistent
# results.
python_version = "3.9"
mypy_path = "src"
strict = true
strict_bytes = true
local_partial_types = true
# Unreachable blocks have been an issue when compiling mypyc, let's try to avoid 'em in the first place.
warn_unreachable = true
implicit_reexport = true
show_error_codes = true
show_column_numbers = true
[[tool.mypy.overrides]]
module = ["pathspec.*", "IPython.*", "colorama.*", "tokenize_rt.*", "uvloop.*", "_black_version.*"]
ignore_missing_imports = true
# CI only checks src/, but in case users are running LSP or similar we explicitly ignore
# errors in test data files.
[[tool.mypy.overrides]]
module = ["tests.data.*"]
ignore_errors = true

View File

@ -14,7 +14,7 @@
import commonmark import commonmark
import yaml import yaml
from bs4 import BeautifulSoup # type: ignore[import-untyped] from bs4 import BeautifulSoup
def main(changes: str, source_version_control: str) -> None: def main(changes: str, source_version_control: str) -> None:

View File

@ -1,48 +0,0 @@
"""
Check that the rev value in the example from ``the_basics.md`` matches
the latest version of Black. This saves us from forgetting to update that
during the release process.
"""
import os
import sys
import commonmark
from bs4 import BeautifulSoup # type: ignore[import-untyped]
def main(changes: str, the_basics: str) -> None:
changes_html = commonmark.commonmark(changes)
changes_soup = BeautifulSoup(changes_html, "html.parser")
headers = changes_soup.find_all("h2")
tags = [header.string for header in headers if header.string != "Unreleased"]
latest_tag = tags[0]
the_basics_html = commonmark.commonmark(the_basics)
the_basics_soup = BeautifulSoup(the_basics_html, "html.parser")
version_examples = [
code_block.string
for code_block in the_basics_soup.find_all(class_="language-console")
if "$ black --version" in code_block.string
]
for tag in tags:
for version_example in version_examples:
if tag in version_example and tag != latest_tag:
print(
"Please set the version in the ``black --version`` "
"examples from ``the_basics.md`` to be the latest one.\n"
f"Expected {latest_tag}, got {tag}.\n"
)
sys.exit(1)
if __name__ == "__main__":
with open("CHANGES.md", encoding="utf-8") as fd:
changes = fd.read()
with open(
os.path.join("docs", "usage_and_configuration", "the_basics.md"),
encoding="utf-8",
) as fd:
the_basics = fd.read()
main(changes, the_basics)

View File

@ -1,231 +0,0 @@
"""Helper script for psf/black's diff-shades Github Actions integration.
diff-shades is a tool for analyzing what happens when you run Black on
OSS code capturing it for comparisons or other usage. It's used here to
help measure the impact of a change *before* landing it (in particular
posting a comment on completion for PRs).
This script exists as a more maintainable alternative to using inline
Javascript in the workflow YAML files. The revision configuration and
resolving, caching, and PR comment logic is contained here.
For more information, please see the developer docs:
https://black.readthedocs.io/en/latest/contributing/gauging_changes.html#diff-shades
"""
import json
import os
import platform
import pprint
import subprocess
import sys
import zipfile
from base64 import b64encode
from io import BytesIO
from pathlib import Path
from typing import Any, Final, Literal
import click
import urllib3
from packaging.version import Version
COMMENT_FILE: Final = ".pr-comment.json"
DIFF_STEP_NAME: Final = "Generate HTML diff report"
DOCS_URL: Final = (
"https://black.readthedocs.io/en/latest/"
"contributing/gauging_changes.html#diff-shades"
)
USER_AGENT: Final = f"psf/black diff-shades workflow via urllib3/{urllib3.__version__}"
SHA_LENGTH: Final = 10
GH_API_TOKEN: Final = os.getenv("GITHUB_TOKEN")
REPO: Final = os.getenv("GITHUB_REPOSITORY", default="psf/black")
http = urllib3.PoolManager()
def set_output(name: str, value: str) -> None:
if len(value) < 200:
print(f"[INFO]: setting '{name}' to '{value}'")
else:
print(f"[INFO]: setting '{name}' to [{len(value)} chars]")
if "GITHUB_OUTPUT" in os.environ:
if "\n" in value:
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
delimiter = b64encode(os.urandom(16)).decode()
value = f"{delimiter}\n{value}\n{delimiter}"
command = f"{name}<<{value}"
else:
command = f"{name}={value}"
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
print(command, file=f)
def http_get(url: str, *, is_json: bool = True, **kwargs: Any) -> Any:
headers = kwargs.get("headers") or {}
headers["User-Agent"] = USER_AGENT
if "github" in url:
if GH_API_TOKEN:
headers["Authorization"] = f"token {GH_API_TOKEN}"
headers["Accept"] = "application/vnd.github.v3+json"
kwargs["headers"] = headers
r = http.request("GET", url, **kwargs)
if is_json:
data = json.loads(r.data.decode("utf-8"))
else:
data = r.data
print(f"[INFO]: issued GET request for {r.geturl()}")
if not (200 <= r.status < 300):
pprint.pprint(dict(r.info()))
pprint.pprint(data)
raise RuntimeError(f"unexpected status code: {r.status}")
return data
def get_main_revision() -> str:
data = http_get(
f"https://api.github.com/repos/{REPO}/commits",
fields={"per_page": "1", "sha": "main"},
)
assert isinstance(data[0]["sha"], str)
return data[0]["sha"]
def get_pr_revision(pr: int) -> str:
data = http_get(f"https://api.github.com/repos/{REPO}/pulls/{pr}")
assert isinstance(data["head"]["sha"], str)
return data["head"]["sha"]
def get_pypi_version() -> Version:
data = http_get("https://pypi.org/pypi/black/json")
versions = [Version(v) for v in data["releases"]]
sorted_versions = sorted(versions, reverse=True)
return sorted_versions[0]
@click.group()
def main() -> None:
pass
@main.command("config", help="Acquire run configuration and metadata.")
@click.argument("event", type=click.Choice(["push", "pull_request"]))
def config(event: Literal["push", "pull_request"]) -> None:
import diff_shades # type: ignore[import-not-found]
if event == "push":
jobs = [{"mode": "preview-changes", "force-flag": "--force-preview-style"}]
# Push on main, let's use PyPI Black as the baseline.
baseline_name = str(get_pypi_version())
baseline_cmd = f"git checkout {baseline_name}"
target_rev = os.getenv("GITHUB_SHA")
assert target_rev is not None
target_name = "main-" + target_rev[:SHA_LENGTH]
target_cmd = f"git checkout {target_rev}"
elif event == "pull_request":
jobs = [
{"mode": "preview-changes", "force-flag": "--force-preview-style"},
{"mode": "assert-no-changes", "force-flag": "--force-stable-style"},
]
# PR, let's use main as the baseline.
baseline_rev = get_main_revision()
baseline_name = "main-" + baseline_rev[:SHA_LENGTH]
baseline_cmd = f"git checkout {baseline_rev}"
pr_ref = os.getenv("GITHUB_REF")
assert pr_ref is not None
pr_num = int(pr_ref[10:-6])
pr_rev = get_pr_revision(pr_num)
target_name = f"pr-{pr_num}-{pr_rev[:SHA_LENGTH]}"
target_cmd = f"gh pr checkout {pr_num} && git merge origin/main"
env = f"{platform.system()}-{platform.python_version()}-{diff_shades.__version__}"
for entry in jobs:
entry["baseline-analysis"] = f"{entry['mode']}-{baseline_name}.json"
entry["baseline-setup-cmd"] = baseline_cmd
entry["target-analysis"] = f"{entry['mode']}-{target_name}.json"
entry["target-setup-cmd"] = target_cmd
entry["baseline-cache-key"] = f"{env}-{baseline_name}-{entry['mode']}"
if event == "pull_request":
# These are only needed for the PR comment.
entry["baseline-sha"] = baseline_rev
entry["target-sha"] = pr_rev
set_output("matrix", json.dumps(jobs, indent=None))
pprint.pprint(jobs)
@main.command("comment-body", help="Generate the body for a summary PR comment.")
@click.argument("baseline", type=click.Path(exists=True, path_type=Path))
@click.argument("target", type=click.Path(exists=True, path_type=Path))
@click.argument("baseline-sha")
@click.argument("target-sha")
@click.argument("pr-num", type=int)
def comment_body(
baseline: Path, target: Path, baseline_sha: str, target_sha: str, pr_num: int
) -> None:
# fmt: off
cmd = [
sys.executable, "-m", "diff_shades", "--no-color",
"compare", str(baseline), str(target), "--quiet", "--check"
]
# fmt: on
proc = subprocess.run(cmd, stdout=subprocess.PIPE, encoding="utf-8")
if not proc.returncode:
body = (
f"**diff-shades** reports zero changes comparing this PR ({target_sha}) to"
f" main ({baseline_sha}).\n\n---\n\n"
)
else:
body = (
f"**diff-shades** results comparing this PR ({target_sha}) to main"
f" ({baseline_sha}). The full diff is [available in the logs]"
f'($job-diff-url) under the "{DIFF_STEP_NAME}" step.'
)
body += "\n```text\n" + proc.stdout.strip() + "\n```\n"
body += (
f"[**What is this?**]({DOCS_URL}) | [Workflow run]($workflow-run-url) |"
" [diff-shades documentation](https://github.com/ichard26/diff-shades#readme)"
)
print(f"[INFO]: writing comment details to {COMMENT_FILE}")
with open(COMMENT_FILE, "w", encoding="utf-8") as f:
json.dump({"body": body, "pr-number": pr_num}, f)
@main.command("comment-details", help="Get PR comment resources from a workflow run.")
@click.argument("run-id")
def comment_details(run_id: str) -> None:
data = http_get(f"https://api.github.com/repos/{REPO}/actions/runs/{run_id}")
if data["event"] != "pull_request" or data["conclusion"] == "cancelled":
set_output("needs-comment", "false")
return
set_output("needs-comment", "true")
jobs = http_get(data["jobs_url"])["jobs"]
job = next(j for j in jobs if j["name"] == "analysis / preview-changes")
diff_step = next(s for s in job["steps"] if s["name"] == DIFF_STEP_NAME)
diff_url = job["html_url"] + f"#step:{diff_step['number']}:1"
artifacts = http_get(data["artifacts_url"])["artifacts"]
comment_artifact = next(a for a in artifacts if a["name"] == COMMENT_FILE)
comment_url = comment_artifact["archive_download_url"]
comment_zip = BytesIO(http_get(comment_url, is_json=False))
with zipfile.ZipFile(comment_zip) as zfile:
with zfile.open(COMMENT_FILE) as rf:
comment_data = json.loads(rf.read().decode("utf-8"))
set_output("pr-number", str(comment_data["pr-number"]))
body = comment_data["body"]
# It's more convenient to fill in these fields after the first workflow is done
# since this command can access the workflows API (doing it in the main workflow
# while it's still in progress seems impossible).
body = body.replace("$workflow-run-url", data["html_url"])
body = body.replace("$job-diff-url", diff_url)
set_output("comment-body", body)
if __name__ == "__main__":
main()

View File

@ -1,74 +0,0 @@
import json
from typing import IO, Any
import click
import black
def generate_schema_from_click(
cmd: click.Command,
) -> dict[str, Any]:
result: dict[str, dict[str, Any]] = {}
for param in cmd.params:
if not isinstance(param, click.Option) or param.is_eager:
continue
assert param.name
name = param.name.replace("_", "-")
result[name] = {}
match param.type:
case click.types.IntParamType():
result[name]["type"] = "integer"
case click.types.StringParamType() | click.types.Path():
result[name]["type"] = "string"
case click.types.Choice(choices=choices):
result[name]["enum"] = choices
case click.types.BoolParamType():
result[name]["type"] = "boolean"
case _:
msg = f"{param.type!r} not a known type for {param}"
raise TypeError(msg)
if param.multiple:
result[name] = {"type": "array", "items": result[name]}
result[name]["description"] = param.help
if param.default is not None and not param.multiple:
result[name]["default"] = param.default
return result
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
@click.option("--schemastore", is_flag=True, help="SchemaStore format")
@click.option("--outfile", type=click.File(mode="w"), help="Write to file")
def main(schemastore: bool, outfile: IO[str]) -> None:
properties = generate_schema_from_click(black.main)
del properties["line-ranges"]
schema: dict[str, Any] = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": (
"https://github.com/psf/black/blob/main/src/black/resources/black.schema.json"
),
"$comment": "tool.black table in pyproject.toml",
"type": "object",
"additionalProperties": False,
"properties": properties,
}
if schemastore:
schema["$id"] = "https://json.schemastore.org/partial-black.json"
# The precise list of unstable features may change frequently, so don't
# bother putting it in SchemaStore
schema["properties"]["enable-unstable-feature"]["items"] = {"type": "string"}
print(json.dumps(schema, indent=2), file=outfile)
if __name__ == "__main__":
main()

View File

@ -1,66 +0,0 @@
"""Generates a width table for Unicode characters.
This script generates a width table for Unicode characters that are not
narrow (width 1). The table is written to src/black/_width_table.py (note
that although this file is generated, it is checked into Git) and is used
by the char_width() function in src/black/strings.py.
You should run this script when you upgrade wcwidth, which is expected to
happen when a new Unicode version is released. The generated table contains
the version of wcwidth and Unicode that it was generated for.
In order to run this script, you need to install the latest version of wcwidth.
You can do this by running:
pip install -U wcwidth
"""
import sys
from collections.abc import Iterable
from os.path import basename, dirname, join
import wcwidth # type: ignore[import-not-found]
def make_width_table() -> Iterable[tuple[int, int, int]]:
start_codepoint = -1
end_codepoint = -1
range_width = -2
for codepoint in range(0, sys.maxunicode + 1):
width = wcwidth.wcwidth(chr(codepoint))
if width <= 1:
# Ignore narrow characters along with zero-width characters so that
# they are treated as single-width. Note that treating zero-width
# characters as single-width is consistent with the heuristics built
# on top of str.isascii() in the str_width() function in strings.py.
continue
if start_codepoint < 0:
start_codepoint = codepoint
range_width = width
elif width != range_width or codepoint != end_codepoint + 1:
yield (start_codepoint, end_codepoint, range_width)
start_codepoint = codepoint
range_width = width
end_codepoint = codepoint
if start_codepoint >= 0:
yield (start_codepoint, end_codepoint, range_width)
def main() -> None:
table_path = join(dirname(__file__), "..", "src", "black", "_width_table.py")
with open(table_path, "w") as f:
f.write(f"""# Generated by {basename(__file__)}
# wcwidth {wcwidth.__version__}
# Unicode {wcwidth.list_versions()[-1]}
from typing import Final
WIDTH_TABLE: Final[list[tuple[int, int, int]]] = [
""")
for triple in make_width_table():
f.write(f" {triple!r},\n")
f.write("]\n")
if __name__ == "__main__":
main()

View File

@ -1,96 +0,0 @@
#!/usr/bin/env python3
# check out every commit added by the current branch, blackify them,
# and generate diffs to reconstruct the original commits, but then
# blackified
import logging
import os
import sys
from subprocess import PIPE, Popen, check_output, run
def git(*args: str) -> str:
return check_output(["git", *args]).decode("utf8").strip()
def blackify(base_branch: str, black_command: str, logger: logging.Logger) -> int:
current_branch = git("branch", "--show-current")
if not current_branch or base_branch == current_branch:
logger.error("You need to check out a feature branch to work on")
return 1
if not os.path.exists(".git"):
logger.error("Run me in the root of your repo")
return 1
merge_base = git("merge-base", "HEAD", base_branch)
if not merge_base:
logger.error(
f"Could not find a common commit for current head and {base_branch}"
)
return 1
commits = git(
"log", "--reverse", "--pretty=format:%H", f"{merge_base}~1..HEAD"
).split()
for commit in commits:
git("checkout", commit, f"-b{commit}-black")
check_output(black_command, shell=True)
git("commit", "-aqm", "blackify")
git("checkout", base_branch, f"-b{current_branch}-black")
for last_commit, commit in zip(commits, commits[1:]):
allow_empty = (
b"--allow-empty" in run(["git", "apply", "-h"], stdout=PIPE).stdout
)
quiet = b"--quiet" in run(["git", "apply", "-h"], stdout=PIPE).stdout
git_diff = Popen(
[
"git",
"diff",
"--binary",
"--find-copies",
f"{last_commit}-black..{commit}-black",
],
stdout=PIPE,
)
git_apply = Popen(
[
"git",
"apply",
]
+ (["--quiet"] if quiet else [])
+ [
"-3",
"--intent-to-add",
]
+ (["--allow-empty"] if allow_empty else [])
+ [
"-",
],
stdin=git_diff.stdout,
)
if git_diff.stdout is not None:
git_diff.stdout.close()
git_apply.communicate()
git("commit", "--allow-empty", "-aqC", commit)
for commit in commits:
git("branch", "-qD", f"{commit}-black")
return 0
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("base_branch")
parser.add_argument("--black_command", default="black -q .")
parser.add_argument("--logfile", type=argparse.FileType("w"), default=sys.stdout)
args = parser.parse_args()
logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler(args.logfile))
logger.setLevel(logging.INFO)
sys.exit(blackify(args.base_branch, args.black_command, logger))

View File

@ -1,244 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
"""
Tool to help automate changes needed in commits during and after releases
"""
import argparse
import logging
import sys
from datetime import datetime
from pathlib import Path
from subprocess import run
LOG = logging.getLogger(__name__)
NEW_VERSION_CHANGELOG_TEMPLATE = """\
## Unreleased
### Highlights
<!-- Include any especially major or disruptive changes here -->
### Stable style
<!-- Changes that affect Black's stable style -->
### Preview style
<!-- Changes that affect Black's preview style -->
### Configuration
<!-- Changes to how Black can be configured -->
### Packaging
<!-- Changes to how Black is packaged, such as dependency requirements -->
### Parser
<!-- Changes to the parser or to version autodetection -->
### Performance
<!-- Changes that improve Black's performance. -->
### Output
<!-- Changes to Black's terminal output and error messages -->
### _Blackd_
<!-- Changes to blackd -->
### Integrations
<!-- For example, Docker, GitHub Actions, pre-commit, editors -->
### Documentation
<!-- Major changes to documentation and policies. Small docs changes
don't need a changelog entry. -->
"""
class NoGitTagsError(Exception): ... # noqa: E701,E761
# TODO: Do better with alpha + beta releases
# Maybe we vendor packaging library
def get_git_tags(versions_only: bool = True) -> list[str]:
"""Pull out all tags or calvers only"""
cp = run(["git", "tag"], capture_output=True, check=True, encoding="utf8")
if not cp.stdout:
LOG.error(f"Returned no git tags stdout: {cp.stderr}")
raise NoGitTagsError
git_tags = cp.stdout.splitlines()
if versions_only:
return [t for t in git_tags if t[0].isdigit()]
return git_tags
# TODO: Support sorting alhpa/beta releases correctly
def tuple_calver(calver: str) -> tuple[int, ...]: # mypy can't notice maxsplit below
"""Convert a calver string into a tuple of ints for sorting"""
try:
return tuple(map(int, calver.split(".", maxsplit=2)))
except ValueError:
return (0, 0, 0)
class SourceFiles:
def __init__(self, black_repo_dir: Path):
# File path fun all pathlib to be platform agnostic
self.black_repo_path = black_repo_dir
self.changes_path = self.black_repo_path / "CHANGES.md"
self.docs_path = self.black_repo_path / "docs"
self.version_doc_paths = (
self.docs_path / "integrations" / "source_version_control.md",
self.docs_path / "usage_and_configuration" / "the_basics.md",
)
self.current_version = self.get_current_version()
self.next_version = self.get_next_version()
def __str__(self) -> str:
return f"""\
> SourceFiles ENV:
Repo path: {self.black_repo_path}
CHANGES.md path: {self.changes_path}
docs path: {self.docs_path}
Current version: {self.current_version}
Next version: {self.next_version}
"""
def add_template_to_changes(self) -> int:
"""Add the template to CHANGES.md if it does not exist"""
LOG.info(f"Adding template to {self.changes_path}")
with self.changes_path.open("r") as cfp:
changes_string = cfp.read()
if "## Unreleased" in changes_string:
LOG.error(f"{self.changes_path} already has unreleased template")
return 1
templated_changes_string = changes_string.replace(
"# Change Log\n",
f"# Change Log\n\n{NEW_VERSION_CHANGELOG_TEMPLATE}",
)
with self.changes_path.open("w") as cfp:
cfp.write(templated_changes_string)
LOG.info(f"Added template to {self.changes_path}")
return 0
def cleanup_changes_template_for_release(self) -> None:
LOG.info(f"Cleaning up {self.changes_path}")
with self.changes_path.open("r") as cfp:
changes_string = cfp.read()
# Change Unreleased to next version
versioned_changes = changes_string.replace(
"## Unreleased", f"## {self.next_version}"
)
# Remove all comments (subheadings are harder - Human required still)
no_comments_changes = []
for line in versioned_changes.splitlines():
if line.startswith("<!--") or line.endswith("-->"):
continue
no_comments_changes.append(line)
with self.changes_path.open("w") as cfp:
cfp.write("\n".join(no_comments_changes) + "\n")
LOG.debug(f"Finished Cleaning up {self.changes_path}")
def get_current_version(self) -> str:
"""Get the latest git (version) tag as latest version"""
return sorted(get_git_tags(), key=lambda k: tuple_calver(k))[-1]
def get_next_version(self) -> str:
"""Workout the year and month + version number we need to move to"""
base_calver = datetime.today().strftime("%y.%m")
calver_parts = base_calver.split(".")
base_calver = f"{calver_parts[0]}.{int(calver_parts[1])}" # Remove leading 0
git_tags = get_git_tags()
same_month_releases = [
t for t in git_tags if t.startswith(base_calver) and "a" not in t
]
if len(same_month_releases) < 1:
return f"{base_calver}.0"
same_month_version = same_month_releases[-1].split(".", 2)[-1]
return f"{base_calver}.{int(same_month_version) + 1}"
def update_repo_for_release(self) -> int:
"""Update CHANGES.md + doc files ready for release"""
self.cleanup_changes_template_for_release()
self.update_version_in_docs()
return 0 # return 0 if no exceptions hit
def update_version_in_docs(self) -> None:
for doc_path in self.version_doc_paths:
LOG.info(f"Updating black version to {self.next_version} in {doc_path}")
with doc_path.open("r") as dfp:
doc_string = dfp.read()
next_version_doc = doc_string.replace(
self.current_version, self.next_version
)
with doc_path.open("w") as dfp:
dfp.write(next_version_doc)
LOG.debug(
f"Finished updating black version to {self.next_version} in {doc_path}"
)
def _handle_debug(debug: bool) -> None:
"""Turn on debugging if asked otherwise INFO default"""
log_level = logging.DEBUG if debug else logging.INFO
logging.basicConfig(
format="[%(asctime)s] %(levelname)s: %(message)s (%(filename)s:%(lineno)d)",
level=log_level,
)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument(
"-a",
"--add-changes-template",
action="store_true",
help="Add the Unreleased template to CHANGES.md",
)
parser.add_argument(
"-d", "--debug", action="store_true", help="Verbose debug output"
)
args = parser.parse_args()
_handle_debug(args.debug)
return args
def main() -> int:
args = parse_args()
# Need parent.parent cause script is in scripts/ directory
sf = SourceFiles(Path(__file__).parent.parent)
if args.add_changes_template:
return sf.add_template_to_changes()
LOG.info(f"Current version detected to be {sf.current_version}")
LOG.info(f"Next version will be {sf.next_version}")
return sf.update_repo_for_release()
if __name__ == "__main__": # pragma: no cover
sys.exit(main())

View File

@ -1,69 +0,0 @@
#!/usr/bin/env python3
import unittest
from pathlib import Path
from shutil import rmtree
from tempfile import TemporaryDirectory
from typing import Any
from unittest.mock import Mock, patch
from release import SourceFiles, tuple_calver # type: ignore
class FakeDateTime:
"""Used to mock the date to test generating next calver function"""
def today(*args: Any, **kwargs: Any) -> "FakeDateTime": # noqa
return FakeDateTime()
# Add leading 0 on purpose to ensure we remove it
def strftime(*args: Any, **kwargs: Any) -> str: # noqa
return "69.01"
class TestRelease(unittest.TestCase):
def setUp(self) -> None:
# We only test on >= 3.12
self.tempdir = TemporaryDirectory(delete=False) # type: ignore
self.tempdir_path = Path(self.tempdir.name)
self.sf = SourceFiles(self.tempdir_path)
def tearDown(self) -> None:
rmtree(self.tempdir.name)
return super().tearDown()
@patch("release.get_git_tags")
def test_get_current_version(self, mocked_git_tags: Mock) -> None:
mocked_git_tags.return_value = ["1.1.0", "69.1.0", "69.1.1", "2.2.0"]
self.assertEqual("69.1.1", self.sf.get_current_version())
@patch("release.get_git_tags")
@patch("release.datetime", FakeDateTime)
def test_get_next_version(self, mocked_git_tags: Mock) -> None:
# test we handle no args
mocked_git_tags.return_value = []
self.assertEqual(
"69.1.0",
self.sf.get_next_version(),
"Unable to get correct next version with no git tags",
)
# test we handle
mocked_git_tags.return_value = ["1.1.0", "69.1.0", "69.1.1", "2.2.0"]
self.assertEqual(
"69.1.2",
self.sf.get_next_version(),
"Unable to get correct version with 2 previous versions released this"
" month",
)
def test_tuple_calver(self) -> None:
first_month_release = tuple_calver("69.1.0")
second_month_release = tuple_calver("69.1.1")
self.assertEqual((69, 1, 0), first_month_release)
self.assertEqual((0, 0, 0), tuple_calver("69.1.1a0")) # Hack for alphas/betas
self.assertTrue(first_month_release < second_month_release)
if __name__ == "__main__":
unittest.main()

3
setup.cfg Normal file
View File

@ -0,0 +1,3 @@
[options]
setup_requires =
setuptools_scm~=6.0.1

118
setup.py Normal file
View File

@ -0,0 +1,118 @@
# Copyright (C) 2020 Łukasz Langa
from setuptools import setup, find_packages
import sys
import os
assert sys.version_info >= (3, 6, 2), "black requires Python 3.6.2+"
from pathlib import Path # noqa E402
CURRENT_DIR = Path(__file__).parent
sys.path.insert(0, str(CURRENT_DIR)) # for setuptools.build_meta
def get_long_description() -> str:
return (
(CURRENT_DIR / "README.md").read_text(encoding="utf8")
+ "\n\n"
+ (CURRENT_DIR / "CHANGES.md").read_text(encoding="utf8")
)
USE_MYPYC = False
# To compile with mypyc, a mypyc checkout must be present on the PYTHONPATH
if len(sys.argv) > 1 and sys.argv[1] == "--use-mypyc":
sys.argv.pop(1)
USE_MYPYC = True
if os.getenv("BLACK_USE_MYPYC", None) == "1":
USE_MYPYC = True
if USE_MYPYC:
mypyc_targets = [
"src/black/__init__.py",
"src/blib2to3/pytree.py",
"src/blib2to3/pygram.py",
"src/blib2to3/pgen2/parse.py",
"src/blib2to3/pgen2/grammar.py",
"src/blib2to3/pgen2/token.py",
"src/blib2to3/pgen2/driver.py",
"src/blib2to3/pgen2/pgen.py",
]
from mypyc.build import mypycify
opt_level = os.getenv("MYPYC_OPT_LEVEL", "3")
ext_modules = mypycify(mypyc_targets, opt_level=opt_level)
else:
ext_modules = []
setup(
name="black",
use_scm_version={
"write_to": "src/_black_version.py",
"write_to_template": 'version = "{version}"\n',
},
description="The uncompromising code formatter.",
long_description=get_long_description(),
long_description_content_type="text/markdown",
keywords="automation formatter yapf autopep8 pyfmt gofmt rustfmt",
author="Łukasz Langa",
author_email="lukasz@langa.pl",
url="https://github.com/psf/black",
project_urls={"Changelog": "https://github.com/psf/black/blob/main/CHANGES.md"},
license="MIT",
py_modules=["_black_version"],
ext_modules=ext_modules,
packages=find_packages(where="src"),
package_dir={"": "src"},
package_data={
"blib2to3": ["*.txt"],
"black": ["py.typed"],
"black_primer": ["primer.json"],
},
python_requires=">=3.6.2",
zip_safe=False,
install_requires=[
"click>=7.1.2",
"platformdirs>=2",
"tomli>=0.2.6,<2.0.0",
"typed-ast>=1.4.2; python_version < '3.8'",
"regex>=2020.1.8",
"pathspec>=0.9.0, <1",
"dataclasses>=0.6; python_version < '3.7'",
"typing_extensions>=3.10.0.0",
# 3.10.0.1 is broken on at least Python 3.10,
# https://github.com/python/typing/issues/865
"typing_extensions!=3.10.0.1; python_version >= '3.10'",
"mypy_extensions>=0.4.3",
],
extras_require={
"d": ["aiohttp>=3.6.0", "aiohttp-cors>=0.4.0"],
"colorama": ["colorama>=0.4.3"],
"python2": ["typed-ast>=1.4.2"],
"uvloop": ["uvloop>=0.15.2"],
"jupyter": ["ipython>=7.8.0", "tokenize-rt>=3.2.0"],
},
test_suite="tests.test_black",
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
],
entry_points={
"console_scripts": [
"black=black:patched_main",
"blackd=blackd:patched_main [d]",
"black-primer=black_primer.cli:main",
]
},
)

Some files were not shown because too many files have changed in this diff Show More