From e5a4a0f6964da530c13bcc21721729f9f575600d Mon Sep 17 00:00:00 2001 From: dwanderson-intel Date: Thu, 27 Oct 2022 14:04:13 -0400 Subject: [PATCH] Upstream force (#8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Drop upper version bounds on dependencies (GH-2718) They mostly cause unnecessary trouble. Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Primer: exclude crashing sqlalchemy file for now (GH-2735) Until we can properly look into and fix it. -> https://github.com/psf/black/issues/2734 * Documentation: include Wing IDE 8 integrations (GH-2733) Wing IDE 8 now supports autoformatting w/ Black natively 🎉 Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Improve CLI reference wording (#2753) * Stubs: preserve blank line between attributes and methods (#2736) * Action: Support running in a docker container (#2748) see: https://github.com/actions/runner/issues/716 * Fix call patterns that contain as-expression on the kwargs (#2749) * Remove Python 2 support (#2740) *blib2to3's support was left untouched because: 1) I don't want to touch parsing machinery, and 2) it'll allow us to provide a more useful error message if someone does try to format Python 2 code. * Enhance `--verbose` (#2526) Black would now echo the location that it determined as the root path for the project if `--verbose` is enabled by the user, according to which it chooses the SRC paths, i.e. the absolute path of the project is `{root}/{src}`. Closes #1880 * Speed up new backtracking parser (#2728) * Fix handling of standalone match/case with newlines/comments (#2760) Resolves #2759 * Change git url for pip installation in README (#2761) * Change git url for pip installation in README Unauthenticated git protocol was disabled recently by Github and should not be used anymore. https://github.blog/2021-09-01-improving-git-protocol-security-github/#no-more-unauthenticated-git * Update CHANGES.md * Change installation url to comply with git security change (#2765) Co-authored-by: Jeffrey Lazar * don't expect changes on poetry (#2769) They just made themselves ESP-compliant in https://github.com/python-poetry/poetry/commit/ecb030e1f0b7c13cc11971f00ee5012e82a892bc * Normalise string prefix order (#2297) Closes #2171 * Don't make redundant copies of the DFA (#2763) * Added decent coloring (#2712) * CI: add diff-shades integration (#2725) Hopefully this makes it much easier to gauge the impacts of future changes! * Dont require typing-extensions in 3.10 (GH-2772) 3.10 ships with TypeGuard which is the newest feature we need. Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * [trivial] Use proper test cases on `unittest` (#2775) * Bump sphinx from 4.3.2 to 4.4.0 in /docs (#2776) Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 4.3.2 to 4.4.0. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/4.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v4.3.2...v4.4.0) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix typo in diff_shades.yml workflow (#2778) * Create --preview CLI flag (#2752) * Fix and speedup diff-shades integration (#2773) * Deprecate ESP and move the functionality under --preview (#2789) * Hint at likely cause of ast parsing failure in error message (#2786) Co-authored-by: Batuhan Taskaya Co-authored-by: Jelle Zijlstra Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Add support for custom python cell magics (#2744) Fixes #2742. This PR adds the ability to configure additional python cell magics. This will allow formatting cells in Jupyter Notebooks that are using custom (python) magics. * Set `click` lower bound to `8.0.0` (#2791) Closes #2774 * add wind technology software projects using black (#2792) * Switch to Furo (#2793) - Add Furo dependency to docs/requirements.txt - Drop a fair bit of theme configuration - Fix the toctree declarations in index.rst - Move stuff around as Furo isn't 100% compatible with Alabaster Furo was chosen as it provides excellent mobile support, user controllable light/dark theming, and is overall easier to read * Allow setting custom cache directory on all platforms (#2739) Fixes #2506 ``XDG_CACHE_HOME`` does not work on Windows. To allow for users to set a custom cache directory on all systems I added a new environment variable ``BLACK_CACHE_DIR`` to set the cache directory. The default remains the same so users will only notice a change if that environment variable is set. The specific use case I have for this is I need to run black on in different processes at the same time. There is a race condition with the cache pickle file that made this rather difficult. A custom cache directory will remove the race condition. I created ``get_cache_dir`` function in order to test the logic. This is only used to set the ``CACHE_DIR`` constant. * Mark Felix and Batuhan as maintainers (#2794) Y'all deserve it :) * Refactor logic for stub empty lines (#2796) This PR is intended to have no change to semantics. This is in preparation for #2784 which will likely introduce more logic that depends on `current_line.depth`. Inlining the subtraction gets rid of offsetting and makes it much easier to see what the result will be. * Mention "skip news" label in CHANGELOG action (#2797) Co-authored-by: hauntsaninja <> * Enable pattern matching by default (#2758) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Allow blackd to be run as a package (#2800) * Remove Beta mentions in README + Docs (#2801) - State we're now stable and that we'll uphold our formatting changes as per policy - Link to The Black Style doc. Co-authored-by: Jelle Zijlstra * Use `magic_trailing_comma` and `preview` for `FileMode` in `fuzz` (#2802) Co-authored-by: Jelle Zijlstra * Make SRC or code mandatory and mutually exclusive (#2360) (#2804) Closes #2360: I'd like to make passing SRC or `--code` mandatory and the arguments mutually exclusive. This will change our (partially already broken) promises of CLI behavior, but I'll comment below. * Hug power operators if its operands are "simple" (#2726) Since power operators almost always have the highest binding power in expressions, it's often more readable to hug it with its operands. The main exception to this is when its operands are non-trivial in which case the power operator will not hug, the rule for this is the following: > For power ops, 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. Fixes GH-538. Closes GH-2095. diff-shades results: https://gist.github.com/ichard26/ca6c6ad4bd1de5152d95418c8645354b Co-authored-by: Diego Co-authored-by: Felix Hildén Co-authored-by: Jelle Zijlstra * properly run ourselves twice (#2807) The previous run-twice logic only affected the stability checks but not the output. Now, we actually output the twice-formatted code. * Fix crash on some power hugging cases (#2806) Found by the fuzzer. Repro case: python -m black -c 'importA;()<<0**0#' * black-primer: stop running it (#2809) At the moment, it's just a source of spurious CI failures and busywork updating the configuration file. Unlike diff-shades, it is run across many different platforms and Python versions, but that doesn't seem essential. We already run unit tests across platforms and versions. I chose to leave the code around for now in case somebody is using it, but CI will no longer run it. * more trailing comma tests (#2810) * Use parentheses on method access on float and int literals (#2799) Co-authored-by: Jelle Zijlstra Co-authored-by: Felix Hildén * Tests for unicode identifiers (#2816) * reorganize release notes for 22.1.0 (#2790) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Elaborate on Python support policy (#2819) * Treat blank lines in stubs the same inside top-level `if` statements (#2820) * torture test (#2815) Fixes #2651. Fixes #2754. Fixes #2518. Fixes #2321. This adds a test that lists a number of cases of unstable formatting that we have seen in the issue tracker. Checking it in will ensure that we don't regress on these cases. * Formalise style preference description (#2818) Closes #1256: I reworded our style docs to be more explicit about the style we're aiming for and how it is changed (or isn't). * Fix arithmetic stability issue (#2817) It turns out "simple_stmt" isn't that simple: it can contain multiple statements separated by semicolons. Invisible parenthesis logic for arithmetic expressions only looked at the first child of simple_stmt. This causes instability in the presence of semicolons, since the next run through the statement following the semicolon will be the first child of another simple_stmt. I believe this along with #2572 fix the known stability issues. * Fix instability due to trailing comma logic (#2572) It was causing stability issues because the first pass could cause a "magic trailing comma" to appear, meaning that the second pass might get a different result. It's not critical. Some things format differently (with extra parens) * Add a test case to torture.py (#2822) Co-authored-by: hauntsaninja <> * Update classifiers to reflect stable (#2823) * Remove test suite from setup.py (#2824) We no longer use it * Fix changelog entries in the wrong release (#2825) * Fix changelog entries in the wrong release (#2825) * Prepare docs for release 22.1.0 (GH-2826) * Adjust `--preview` documentation (#2833) * Exclude __pypackages__ by default (GH-2836) PDM uses this as part of not-accepted-yet PEP 582. * Soft comparison of --required-version (#2832) Co-authored-by: Jelle Zijlstra Co-authored-by: Felix Hildén * release process: formalize the changelog template (#2837) I did this manually for the last few releases and I think it's going to be helpful in the future too. Unfortunately this adds a little more work during the release (sorry @cooperlees). This change will also improve the merge conflict situation a bit, because changes to different sections won't merge conflict. For the last release, the sections were in a kind of random order. In the template I put highlights and "Style" first because they're most important to users, and alphabetized the rest. * Surface links to Stability Policy (GH-2848) * Isolate command line tests from user-level config (#2851) * Update description for GitHub Action `options:` argument (GH-2858) It was missing --diff as one of the default arguments passed. * Create indentation FAQ entry (#2855) Co-authored-by: Jelle Zijlstra * Bump sphinx-copybutton from 0.4.0 to 0.5.0 in /docs (#2871) Bumps [sphinx-copybutton](https://github.com/executablebooks/sphinx-copybutton) from 0.4.0 to 0.5.0. - [Release notes](https://github.com/executablebooks/sphinx-copybutton/releases) - [Changelog](https://github.com/executablebooks/sphinx-copybutton/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/sphinx-copybutton/compare/v0.4.0...v0.5.0) --- updated-dependencies: - dependency-name: sphinx-copybutton dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add Django in 'used by' section in Readme (#2875) * Add Django in 'used by' section in Readme * Fix Readme issue * Avoid crashing when the user has no homedir (#2814) * Order the disabled error codes for pylint (GH-2870) Just make them alphabetical. * Fix typo in file_collection_and_discovery.md (GH-2860) "you your" -> "your" Co-authored-by: Felix Hildén * Isolate command line tests for notebooks from user-level config (#2854) * correct Vim integration code (#2853) - use `Black` directly: the commands an autocommand runs are Ex commands, so no execute or colon is necessary. - use an `augroup` (best practice) to prevent duplicate autocommands from hindering performance. * Add special config verbose log case when black is using user-level config (#2861) * Bump furo from 2022.1.2 to 2022.2.14.1 in /docs (GH-2892) Bumps [furo](https://github.com/pradyunsg/furo) from 2022.1.2 to 2022.2.14.1. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2022.01.02...2022.02.14.1) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Format ourselves in preview mode (#2889) * separate CHANGELOG section for preview style (#2890) * fix new formatting issue (#2895) Race between #2889 and another PR. * README: fix "Pragmatism" link target (#2901) Fixes #2897 * replace md5 with sha256 (#2905) MD5 is unavailable on systems with active FIPS mode. That makes black crash when run on such systems. * Move test for g:load_black to improve plugin performance (GH-2896) If a vim/neovim user wishes to suppress loading the vim plugin by setting g:load_black in their VIMRC (for me, Arch linux automatically adds the plugin to Neovim's RTP, even though I'm not using it), the current location of the test comes after a call to has('python3'). This adds, in my tests, between 35 and 45 ms to Vim load time (which I know isn't a lot but it's also unnecessary). Moving the call to `exists('g:load_black')` to before the call to `has('python3')` removes this unnecessary test and speeds up loading. Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Bump furo from 2022.2.14.1 to 2022.3.4 in /docs (#2906) Bumps [furo](https://github.com/pradyunsg/furo) from 2022.2.14.1 to 2022.3.4. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2022.02.14.1...2022.03.04) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Allow `for`'s target expression to be starred (#2879) Fixes #2878 * Bump actions/checkout from 2 to 3 (#2909) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump actions/setup-python from 2 to 3 (#2908) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 3. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cooper Lees * Fix handling of Windows junctions in normalize_path_maybe_ignore (#2904) Fixes #2569 * Use tomllib on Python 3.11 (#2903) * Bump mypy, flake8, and pre-commit-hooks in pre-commit (GH-2922) * Farewell black-primer, it was nice knowing you (#2924) Enjoy your retirement at https://github.com/cooperlees/black-primer * Remove power hugging formatting from preview (#2928) It is falsely placed in preview features and always formats the power operators, it was added in #2789 but there is no check for formatting added along with it. * Update pylint config docs (#2931) * dont skip formatting #%% (#2919) Fixes #2588 * stub style: remove some possible future changes (#2940) Fixes #2938. All of these suggested future changes are out of scope for an autoformatter and should be handled by a linter instead. * Github now supports .git-blame-ignore-revs (GH-2948) It's in beta. https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view * Avoid magic-trailing-comma in single-element subscripts (#2942) Closes #2918. * Remove unnecessary parentheses from tuple unpacking in `for` loops (#2945) * Resolve new flake8-bugbear errors (B020) (GH-2950) Fixes a couple places where we were using the same variable name as we are iterating over. Co-authored-by: Jelle Zijlstra * Remove unnecessary parentheses from `except` clauses (#2939) Co-authored-by: Jelle Zijlstra * Enforce no formatting changes for PRs via CI (GH-2951) Now PRs will run two diff-shades jobs, "preview-changes" which formats all projects with preview=True, and "assert-no-changes" which formats all projects with preview=False. The latter also fails if any changes were made. Pushes to main will only run "preview-changes" Also the workflow_dispatch feature was dropped since it was complicating everything for little gain. * Bump sphinx from 4.4.0 to 4.5.0 in /docs (GH-2959) Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/4.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v4.4.0...v4.5.0) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix _unicodefun patch code for Click 8.1.0 (#2966) Fixes #2964 * Prepare release 22.3.0 (#2968) * Bump actions/cache from 2.1.7 to 3 (GH-2962) Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v2.1.7...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Keep tests working w/ upcoming aiohttp 4.0.0 (#2974) aiohttp.test_utils.unittest_run_loop was deprecated since aiohttp 3.8 and aiohttp 4 (which isn't a thing quite yet) removes it. To maintain compatibility with the full range of versions we declare to support, test_blackd.py will now define a no-op replacement if it can't be imported. Also, mypy is painfully slow to use without a cache, let's reenable it. * Convert `index.rst` and `license.rst` to markdown (#2852) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Add # type: ignore for click._unicodefun import (#2981) * Remove click pin in diff-shades workflow (#2979) Click 8.1.1 was released with a fix for pallets/click#2227. * Bump peter-evans/find-comment from 1.3.0 to 2 (#2960) Bumps [peter-evans/find-comment](https://github.com/peter-evans/find-comment) from 1.3.0 to 2. - [Release notes](https://github.com/peter-evans/find-comment/releases) - [Commits](https://github.com/peter-evans/find-comment/compare/d2dae40ed151c634e4189471272b57e76ec19ba8...1769778a0c5bd330272d749d12c036d65e70d39d) --- updated-dependencies: - dependency-name: peter-evans/find-comment dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump peter-evans/create-or-update-comment from 1.4.5 to 2 (#2961) Bumps [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) from 1.4.5 to 2. - [Release notes](https://github.com/peter-evans/create-or-update-comment/releases) - [Commits](https://github.com/peter-evans/create-or-update-comment/compare/a35cf36e5301d70b76f316e867e7788a55a31dae...c9fcb64660bc90ec1cc535646af190c992007c32) --- updated-dependencies: - dependency-name: peter-evans/create-or-update-comment dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * try-except tomllib import (#2987) See #2965 I left the version check in place because mypy doesn't generally like try-excepted imports. * Fix broken link in README.md (#2989) Broken when we converted some more RST docs to MyST * Remove unnecessary parentheses from `with` statements (#2926) Co-authored-by: Jelle Zijlstra Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Update test_black.shhh_click test for click 8+ (#2993) The 8.0.x series renamed its "die on LANG=C" function and the 8.1.x series straight up deleted it. Unfortunately this makes this test type check cleanly hard, so we'll just lint with click 8.1+ (the pre-commit hook configuration was changed mostly to just evict any now unsupported mypy environments) * Update FAQ: Mention formatting of custom jupyter cell magic (#2982) Co-authored-by: Jelle Zijlstra Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Top PyPI Packages: Use 30-days data, 365 is no longer available (#2995) * Output python version and implementation as part of `--version` flag (#2997) Example: black, 22.1.1.dev56+g421383d.d20220405 (compiled: no) Python (CPython) 3.9.12 Co-authored-by: Batuhan Taskaya * Better manage return annotation brackets (#2990) Allows us to better control placement of return annotations by: a) removing redundant parens b) moves very long type annotations onto their own line Co-authored-by: Jelle Zijlstra * Remove redundant parentheses around awaited coroutines/tasks (#2991) This is a tricky one as await is technically an expression and therefore in certain situations requires brackets for operator precedence. However, the vast majority of await usage is just await some_coroutine(...) and similar in format to return statements. Therefore this PR removes redundant parens around these await expressions. Co-authored-by: Jelle Zijlstra Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Correctly handle fmt: skip comments without internal spaces (#2970) Co-authored-by: Jelle Zijlstra * Explain our use of mypyc in the FAQ (#3002) I realized we don't have a FAQ entry about this, let's change that so compiled: yes/no doesn't surprise as many people :) Co-authored-by: Jelle Zijlstra * Bump actions/upload-artifact from 2 to 3 (#3004) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Quote "black[jupyter]" in README.md (#3007) * Bump furo from 2022.3.4 to 2022.4.7 in /docs (#3003) Bumps [furo](https://github.com/pradyunsg/furo) from 2022.3.4 to 2022.4.7. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2022.03.04...2022.04.07) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Quote black[jupyter] and black[d] in installation docs (#3006) We just got someone on Discord who was confused because the command as written caused their shell to try to do command expansion. Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Make ipynb tests compatible with ipython 8.3.0+ (#3008) * Support 3.11 / PEP 654 syntax (#3016) * Updated Black Docker Hub link in docs (#3023) Fixes #3022 * Fix strtobool function (#3025) * Fix strtobool function for vim plugin * Update CHANGES.md Co-authored-by: Cooper Lees * Stop pinning lark-parser (#3041) - Latest version works more Test: `tox -e fuzz` * Bump myst-parser from 0.16.1 to 0.17.2 in /docs (#3019) Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 0.16.1 to 0.17.2. - [Release notes](https://github.com/executablebooks/MyST-Parser/releases) - [Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/MyST-Parser/compare/v0.16.1...v0.17.2) --- updated-dependencies: - dependency-name: myst-parser dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Set permissions for GitHub actions (#3043) Restrict the GitHub token permissions only to the required ones; this way, even if the attackers will succeed in compromising your workflow, they won’t be able to do much. - Included permissions for the action. https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> * Move imports of `ThreadPoolExecutor` into `reformat_many()`, allowing Black-in-the-browser (#3046) This is a slight perf win for use-cases that don't invoke `reformat_many()`, but more importantly to me today it means I can use Black in pyscript. * Docs: clarify fmt:on/off requirements (#2985) (#3048) * Put closing quote on a separate line if docstring is too long (#3044) Fixes #1632 Co-authored-by: Felix Hildén * Read simple data cases automatically (#3034) Co-authored-by: Felix Hildén * Bump docker/setup-qemu-action from 1 to 2 (#3056) Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 1 to 2. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docker/build-push-action from 2 to 3 (#3057) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 2 to 3. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docker/login-action from 1 to 2 (#3059) Bumps [docker/login-action](https://github.com/docker/login-action) from 1 to 2. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docker/setup-buildx-action from 1 to 2 (#3058) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1 to 2. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v1...v2) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Remove hard coded test cases (#3062) * Document new Microsoft Black Formatter extension for VSCode (#3063) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Add more examples to exclude files in addition to the defaults (#3070) Co-authored-by: Jelle Zijlstra * Implement support for PEP 646 (#3071) * Add script to ease migration to black (#3038) * Add script to ease migration to black * Update CHANGES.md Co-authored-by: Cooper Lees * Fix minor typo (#3096) * Bump pre-commit/action from 2.0.3 to 3.0.0 (#3108) Bumps [pre-commit/action](https://github.com/pre-commit/action) from 2.0.3 to 3.0.0. - [Release notes](https://github.com/pre-commit/action/releases) - [Commits](https://github.com/pre-commit/action/compare/v2.0.3...v3.0.0) --- updated-dependencies: - dependency-name: pre-commit/action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Remove newline after code block open (#3035) Co-authored-by: Jelle Zijlstra * Update documentation dependencies (#3118) Furo, myst-parser, and Sphinx (had to pin docutils due to sphinx breakage) * Use is_number_token instead of assertion (#3069) * Bump actions/setup-python from 3 to 4 (#3121) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Test run black on self (#3114) * Add run_self environment in tox * Add run_self task as part of the lint CI flow * Remove hard coded sources list * Remove black from pre-commit Co-authored-by: Cooper Lees * Replace link to Requests documentation (#3125) * Bump sphinx from 5.0.1 to 5.0.2 in /docs (#3128) Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.0.1 to 5.0.2. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.0.1...v5.0.2) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Only call get_future_imports when needed (#3135) * Bump furo from 2022.6.4.1 to 2022.6.21 in /docs (#3138) Bumps [furo](https://github.com/pradyunsg/furo) from 2022.6.4.1 to 2022.6.21. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2022.06.04.1...2022.06.21) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update preview style docs to include recent changes (#3136) Covers GH-2926, GH-2990, GH-2991, and GH-3035. Co-authored-by: Jelle Zijlstra Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Prepare docs for release 22.6.0 (#3139) * Fix typo in CHANGES.md (#3142) * Use RTD's new build process and config (#3149) See the deprecation notice: https://docs.readthedocs.io/en/stable/config-file/v2.html#python-version * Stability policy: permit exceptional changes for unformatted code (#3155) * Recommend using BlackConnect in IntelliJ IDEs (#3150) * Recommend using BlackConnect in IntelliJ IDEs * IntelliJ IDEs integration docs: improve formatting * Add changelog for recommending BlackConnect * IntelliJ IDEs integration docs: improve formatting * Apply suggestions from code review Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Fix indentation * Apply italic to Black name Consequently with other places in the document * Move CHANGELOG entry to Unreleased section * IntelliJ IDEs integration docs: bring back a point with formatting a file * IntelliJ IDEs integration docs: fix extra whitespace and linebreak Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Copy over comments when hugging power ops (#2874) Otherwise they'd be deleted which was a regression in 22.1.0 (oops! my bad!). Also type comments are now tracked in the AST safety check on all compatible platforms to error out if this happens again. Overall the line rewriting code has been rewritten to do "the right thing (tm)", I hope this fixes other potential bugs in the code (fwiw I got to drop the bugfix in blib2to3.pytree.Leaf.clone since now bracket metadata is properly copied over). Fixes #2873 * Don't (ever) put a single-char closing docstring quote on a new line (#3166) Doing so is invalid. Note this only fixes the preview style since the logic putting closing docstring quotes on their own line if they violate the line length limit is quite new. Co-authored-by: Jelle Zijlstra * Add warning to not run blackd publicly in docs (#3167) Co-authored-by: Jelle Zijlstra * Move to explicitly creating a new loop (#3164) * Move to explicitly creating a new loop - >= 3.10 add a warning that `get_event_loop` will not automatically create a loop - Move to explicit API Test: - `python3.11 -m venv --upgrade-deps /tmp/tb` - `/tmp/tb/bin/pip install -e .` - Install deps and no blackd as aiohttp + yarl can't build still with 3.11 - https://github.com/aio-libs/aiohttp/issues/6600 - `export PYTHONWARNINGS=error` ``` cooper@l33t:~/repos/black$ /tmp/tb/bin/black . All done! ✨ 🍰 ✨ 44 files left unchanged. ``` Fixes #3110 * Add to CHANGES.md * Fix a cooper typo yet again * Set default asyncio loop to our explicitly created one + unset on exit * Update CHANGES.md Fix my silly typo. Co-authored-by: Thomas Grainger Co-authored-by: Cooper Ry Lees Co-authored-by: Thomas Grainger * Actually disable docstring prefix normalization with -S + fix instability (#3168) The former was a regression I introduced a long time ago. To avoid changing the stable style too much, the regression is only fixed if --preview is enabled Annoyingly enough, as we currently always enforce a second format pass if changes were made, there's no good way to prove the existence of the docstring quote normalization instability issue. For posterity, here's one failing example: --- source +++ first pass @@ -1,7 +1,7 @@ def some_function(self): - '''' + """ ' - ''' + """ pass --- first pass +++ second pass @@ -1,7 +1,7 @@ def some_function(self): - """ ' + """' """ pass Co-authored-by: Jelle Zijlstra * Fix typo in config docs for --extend-exclude (#3170) The old regex in the example was invalid and caused an error on startup. * configure strict pytest and filterwarnings=['error', ... (#3173) * configure strict pytest * ignore current warnings * Add pypy-3.8 to test matrix (#3174) * Improve warning filtering in tests (#3175) * Fix the handling of `# fmt: skip` when it's at a colon line (#3148) When the Leaf node with `# fmt: skip` is a NEWLINE inside a `suite` Node, the nodes to ignore should be from the siblings of the parent `suite` Node. There is a also a special case for the ASYNC token, where it expands to the grandparent Node where the ASYNC token is. This fixes GH-2646, GH-3126, GH-2680, GH-2421, GH-2339, and GH-2138. * Fix an infinite loop when using `# fmt: on/off` ... (#3158) ... in the middle of an expression or code block by adding a missing return. Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> Co-authored-by: Jelle Zijlstra * Use underscores instead of a space in a test file's name (#3180) ... for *consistency* * Bump sphinx from 5.0.2 to 5.1.0 in /docs (#3183) Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.0.2 to 5.1.0. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.0.2...v5.1.0) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add isort to linting toolchain Co-authored-by: Shivansh-007 * Reformat codebase with isort * Bump pre-commit hooks (#3191) * Consolidate test CI and add concurrency limits (#3189) * Vim plugin: prefix messages with "Black: " (#3194) As mentioned in GH-3185, when using Black as a Vim plugin, especially automatically on save, the plugin's messages can be confusing, as nothing indicates that they come from Black. * Remove blib2to3 grammar cache logging (#3193) As error logs are emitted often (they happen when Black's cache directory is created after blib2to3 tries to write its cache) and cause issues to be filed by users who think Black isn't working correctly. These errors are expected for now and aren't a cause for concern so let's remove them to stop worrying users (and new issues from being opened). We can improve the blib2to3 caching mechanism to write its cache at the end of a successful command line invocation later. * Add sanity check to executable CD + more (#3190) Building executables without any testing is quite sketchy, let's at least verify they won't crash on startup and format Black's own codebase. Also replaced "binaries" with "executables" since it's clearer and won't be confused with mypyc. Finally, I added colorama so all Windows users can get colour. * Move fuzz.py to scripts/ (#3199) * Bump sphinx from 5.1.0 to 5.1.1 in /docs (#3201) Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.1.0...v5.1.1) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * makes install available for all users in docker image (#3202) * makes install available for all users in docker image moves the installation path from /root/.local to a virtualenv. this way we still get the lightweight multistage build without excluding non-root users. * adds changelog entry for docker-image fix A changelog entry has been added under the Integration subheader * changes dockerfile to use the venv activate script we are now using the inbuilt venv activate script, as well as explicitly mentioning the binary location in the entrypoint cmd. Co-authored-by: Nicolò Co-authored-by: Cooper Lees * Remove invalid syntax in docstrings -S --preview test (#3205) uR is not a legal string prefix, so this test breaks (AssertionError: cannot use --safe with this file; failed to parse source file AST: invalid syntax) if changed to one in which the file is changed. I've changed the last test to have u alone, and added an R to the test above instead. * Use debug f-strings for feature detection (#3215) Fixes GH-2907. * Use --no-implicit-optional for type checking (#3220) This makes type checking PEP 484 compliant (as of 2018). mypy will change its defaults soon. See: https://github.com/python/mypy/issues/9091 https://github.com/python/mypy/pull/13401 * add preview option support for blackd (#3217) Fixes #3195 Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Port & upstream mypyc wheel build workflow (#3197) * Strip trailing commas in subscripts with -C (#3209) Fixes #2296, #3204 * Update email (#3235) This file gets scraped a lot, so create a distinct email for potential spam. * Add passing 3.11 CI by exempting blackd tests (#3234) - Had to exempt blackd tests for now due to aiohttp - Skip by using `sys.version_info` tuple - aiohttp does not compile in 3.11 yet - refer to #3230 - Add a deadsnakes ubuntu workflow to run 3.11-dev to ensure we don't regress - Have it also format ourselves Test: - `tox -e 311` Co-authored-by: Cooper Ry Lees Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Fix a string merging/split issue caused by standalone comments. (#3227) Fixes #2734: a standalone comment causes strings to be merged into one far too long (and requiring two passes to do so). Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Remove hacky subprocess call in action.yml (#3226) Updates action.yml to use the alternative $GITHUB_ACTION_PATH variable instead of the original ${{ github.action_path }} which caused issues with bash on the Windows runners. This removes the need for a Python subprocess to call the main.py script. * Fix misdetection of project root with `--stdin-filename` (#3216) There are a number of places this behaviour could be patched, for instance, it's quite tempting to patch it in `get_sources`. However I believe we generally have the invariant that project root contains all files we want to format, in which case it seems prudent to keep that invariant. This also improves the accuracy of the "sources to be formatted" log message with --stdin-filename. Fixes GH-3207. * Lazily import parallelized format modules `black.reformat_many` depends on a lot of slow-to-import modules. When formatting simply a single file, the time paid to import those modules is totally wasted. So I moved `black.reformat_many` and its helpers to `black.concurrency` which is now *only* imported if there's more than one file to reformat. This way, running Black over a single file is snappier Here are the numbers before and after this patch running `python -m black --version`: - interpreted: 411 ms +- 9 ms -> 342 ms +- 7 ms: 1.20x faster - compiled: 365 ms +- 15 ms -> 304 ms +- 7 ms: 1.20x faster Co-authored-by: Fabio Zadrozny * Load .gitignore and exclude regex at time of use Loading .gitignore and compiling the exclude regex can take more than 15ms. We shouldn't and don't need to pay this cost if we're simply formatting files given on the command line directly. I would've loved to lazily import pathspec, but the patch won't be clean until the file collection and discovery logic is refactored first. Co-authored-by: Fabio Zadrozny * Delay worker count determination os.cpu_count() can return None (sounds like a super arcane edge case though) so the type annotation for the `workers` parameter of `black.main` is wrong. This *could* technically cause a runtime TypeError since it'd trip one of mypyc's runtime type checks so we might as well fix it. Reading the documentation (and cross-checking with the source code), you are actually allowed to pass None as `max_workers` to `concurrent.futures.ProcessPoolExecutor`. If it is None, the pool initializer will simply call os.cpu_count() [^1] (defaulting to 1 if it returns None [^2]). It'll even round down the worker count to a level that's safe for Windows. ... so theoretically we don't even need to call os.cpu_count() ourselves, but our Windows limit is 60 (unlike the stdlib's 61) and I'd prefer not accidentally reintroducing a crash on machines with many, many CPU cores. [^1]: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor [^2]: https://github.com/python/cpython/blob/a372a7d65320396d44e8beb976e3a6c382963d4e/Lib/concurrent/futures/process.py#L600 * Add parens around implicit string concatenations where it increases readability (#3162) Adds parentheses around implicit string concatenations when it's inside a list, set, or tuple. Except when it's only element and there's no trailing comma. Looking at the order of the transformers here, we need to "wrap in parens" before string_split runs. So my solution is to introduce a "collaboration" between StringSplitter and StringParenWrapper where the splitter "skips" the split until the wrapper adds the parens (and then the line after the paren is split by StringSplitter) in another pass. I have also considered an alternative approach, where I tried to add a different "string paren wrapper" class, and it runs before string_split. Then I found out it requires a different do_transform implementation than StringParenWrapper.do_transform, since the later assumes it runs after the delimiter_split transform. So I stopped researching that route. Originally function calls were also included in this change, but given missing commas should usually result in a runtime error and the scary amount of changes this cause on downstream code, they were removed in later revisions. * Use strict mypy checking (#3222) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Use .gitignore files in the initial source directories (#3237) Solves https://github.com/psf/black/issues/2598 where Black wouldn't use .gitignore at folder/.gitignore if you ran `black folder` for example. Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Improve & update release process to reflect recent changes (#3242) - Formalise release cadence guidelines - Overhaul release steps to be easier to follow and more thorough - Reorder changelog template to something more sensible - Update release automation docs to reflect recent improvements (notably the addition of in-repo mypyc wheel builds) Co-authored-by: Felix Hildén Co-authored-by: Jelle Zijlstra * Update stable branch after publishing to PyPI (#3223) We've decided to a) convert stable back into a branch and b) to update it immediately as part of the release process. We may as well automate it. And about going back to a branch ... Git tags are not the right tool, at all[^1]. They come with the expectation that they will never change. Things will not work as expected if they do change, doubly so if they change regularly. Once you pull stable from the remote and it's copied in your local repository, no matter how many times you run git pull you'll never see it get updated automatically. Your only recourse is to delete the tag via `git tag -d stable` before pulling. This gets annoying really quickly since stable is supposed to be the solution for folks "who want to move along as Black developers deem the newest version reliable."[^2] See this comment for how this impacts users using our Vim plugin[^3]. It also affects us developers[^4]. If you have stable locally, once we cut a new release and update the stable tag, a simple `git pull` / `git fetch` will not pull down the updated stable tag. Unless you remember to delete stable before pulling, stable will become stale and useless. You can argue this is a good thing ("people should explicitly opt into updating stable"), but IMO it does not match user expectations nor developer expectations[^5]. Especially since not all our integrations that use stable are bound by this security measure, for example our GitHub Action (since it does a clean fetch of the repository every time it's used). I believe consistency would be good here. Finally, ever since we switched to a tag, we've been facing issues with ReadTheDocs not picking up updates to stable unless we force a rebuild. The initial rebuild on the stable update just pulls the commit the tag previously pointed to. I'm not sure if switching back to a branch will fix this, but I'd wager it will. [^1]: https://git-scm.com/docs/git-tag#_on_re_tagging [^2]: https://black.readthedocs.io/en/stable/contributing/release_process.html#moving-the-stable-tag [^3]: https://github.com/psf/black/issues/2503#issuecomment-1196357379 [^4]: In fairness, most folks working on Black probably don't use the `stable` ref anyway, especially us maintainers who'd know what is the latest version by heart, but it'd still be nice to make it usable for local dev though. [^5]: Also what benefit does a `stable` ref have over explicit version tags like `22.6.0`? If you're going to opt into some odd pin mechanism, might as well use explicit version tags for clarity and consistency. * Prepare docs for release 22.8.0 (#3248) * docs: adds ExitStack alternative to future_style.md (#3247) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> Co-authored-by: Jelle Zijlstra * Add preview flag to Vim plugin (#3246) This allows the configuration of the --preview flag in the Vim plugin. * Mitigate deprecation of aiohttp's `@middleware` decorator (#3259) This is deprecated since aiohttp 4.0. If it doesn't exist just define a no-op decorator that does nothing (after the other aiohttp imports though!). By doing this, it's safe to ignore the DeprecationWarning without needing to require the latest aiohttp once they remove `@middleware`. * Move 3.11 tests to install aiohttp without C extensions (#3258) * Move 311 tests to install aiohttp without C extensions - Configure tox to install aiohttp without extensions - i.e. use `AIOHTTP_NO_EXTENSIONS=1` for pip install - This allows us to reenable blackd tests that use aiohttp testing helpers etc. - Had to ignore `cgi` module deprecation warning - Filed issue for aiohttp to fix: https://github.com/aio-libs/aiohttp/issues/6905 Test: - `/tmp/tb/bin/tox -e 311` * Fix formatting + linting * Add latest aiohttp for loop fix + Try to exempt deprecation warning but failed - will ask for help * Remove unnecessary warning ignore Co-authored-by: Cooper Ry Lees Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * [FIX] migrate-black.py: don't fail on binary files (#3266) * Fix a crash on dicts with paren-wrapped long string keys (#3262) Fix a crash when formatting some dicts with parenthesis-wrapped long string keys. When LL[0] is an atom string, we need to check the atom node's siblings instead of LL[0] itself, e.g.: dictsetmaker atom STRING '"This is a really long string that can\'t be expected to fit in one line and is used as a nested dict\'s key"' /atom COLON ':' atom LSQB ' ' '[' listmaker STRING '"value"' COMMA ',' STRING ' ' '"value"' /listmaker RSQB ']' /atom COMMA ',' /dictsetmaker * Improve order of paragraphs on line splitting (#3270) These two paragraphs were tucked away at the end of the section, after the diversion on backslashes. I nearly missed the first paragraph and opened a nonsense issue, and I think the second belongs higher up with it too. * Fix mypyc build errors on newer manylinux2014_x86_64 images (#3272) Make sure `gcc` is installed in the build env The mypyc build requires `gcc` to be installed even if it's being built with `clang`, otherwise `clang` fails to find `libgcc`. * Bump furo from 2022.6.21 to 2022.9.15 in /docs (#3277) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Build mypyc wheels for CPython 3.11 (#3276) Bumps cibuildwheel from 2.8.1 to 2.10.0 which has 3.11 building enabled by default. Unfortunately mypyc errors out on 3.11: src/black/files.py:29:9: error: Name "tomllib" already defined (by an import) [no-redef] ... so we have to also hide the fallback import of tomli on older 3.11 alphas from mypy[c]. * Make context manager examples in future style docs consistent (#3274) * Support version specifiers in GH action (#3265) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Fix a crash when `# fmt: on` is used on a different block level than `# fmt: off` (#3281) Previously _Black_ produces invalid code because the `# fmt: on` is used on a different block level. While _Black_ requires `# fmt: off` and `# fmt: on` to be used at the same block level, incorrect usage shouldn't cause crashes. The formatting behavior this PR introduces is, the code below the initial `# fmt: off` block level will be turned off for formatting, when `# fmt: on` is used on a different level or there is no `# fmt: on`. This also matches the current behavior when `# fmt: off` is used at the top-level without a matching `# fmt: on`, it turns off formatting for everything below `# fmt: off`. - Fixes #2567 - Fixes #3184 - Fixes #2985 - Fixes #2882 - Fixes #2232 - Fixes #2140 - Fixes #1817 - Fixes #569 * Make README logo link to docs (#3285) docs: Make README logo link to docs * Switch build backend to Hatchling (#3233) This implements PEP 621, obviating the need for `setup.py`, `setup.cfg`, and `MANIFEST.in`. The build backend Hatchling (of which I am a maintainer in the PyPA) is now used as that is the default in the official Python packaging tutorial. Hatchling is available on all the major distribution channels such as Debian, Fedora, and many more. ## Python support The earliest supported Python 3 version of Hatchling is 3.7, therefore I've also set that as the minimum here. Python 3.6 is EOL and other build backends like flit-core and setuptools also dropped support. Python 3.6 accounted for 3-4% of downloads in the last month. ## Plugins Configuration is now completely static with the help of 3 plugins: ### Readme hynek's hatch-fancy-pypi-readme allows for the dynamic construction of the readme which was previously coded up in `setup.py`. Now it's simply: ```toml [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/markdown" fragments = [ { path = "README.md" }, { path = "CHANGES.md" }, ] ``` ### Versioning hatch-vcs is currently just a wrapper around setuptools-scm (which despite the legacy naming is actually now decoupled from setuptools): ```toml [tool.hatch.version] source = "vcs" [tool.hatch.build.hooks.vcs] version-file = "src/_black_version.py" template = ''' version = "{version}" ''' ``` ### mypyc hatch-mypyc offers many benefits over the existing approach: - No need to manually select files for inclusion - Avoids the need for the current CI workaround for https://github.com/mypyc/mypyc/issues/946 - Intermediate artifacts (like `build/`) from setuptools and mypyc itself no longer clutter the project directory - Runtime dependencies required at build time no longer need to be manually redeclared as this is a built-in option of Hatchling Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> Co-authored-by: Jelle Zijlstra * Fix outdated references to 3.6 and run pyupgrade (#3286) I also missed the accidental removal of the 3.11 classifier in the PR. * Always call freeze_support() if sys.frozen is True (#3275) * Bump actions/upload-artifact from 2 to 3 (#3289) updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump sphinx from 5.1.1 to 5.2.1 in /docs (#3288) Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.1.1 to 5.2.1. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.1.1...v5.2.1) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add option to format Jupyter Notebooks in GitHub Action (#3282) To run the formatter on Jupyter Notebooks, Black must be installed with an extra dependency (`black[jupyter]`). This commit adds an optional argument to install Black with this dependency when using the official GitHub Action [1]. To enable the formatter on Jupyter Notebooks, just add `jupyter: true` as an argument. Feature requested at [2]. [1]: https://black.readthedocs.io/en/stable/integrations/github_actions.html [2]: https://github.com/psf/black/issues/3280 Signed-off-by: Antonio Ossa Guerra * Mention CHANGES.md in PR template explicitly (#3295) This makes the location more explicit which hopefully makes the PR process smoother for other first time contributors. Co-authored-by: Jelle Zijlstra * Bump pypa/cibuildwheel from 2.10.0 to 2.10.2 (#3290) updated-dependencies: - dependency-name: pypa/cibuildwheel dependency-type: direct:production update-type: version-update:semver-patch Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Enable build isolation under CIWB (#3297) No idea how this is still here after the Hatchling PR, but it is no longer useful and is breaking the build. * Add .ipynb_checkpoints to DEFAULT_EXCLUDES (#3293) Jupyter creates a checkpoint file every single time you create an .ipynb file, and then it updates the checkpoint file every single time you manually save your progress for the initial .ipynb. These checkpoints are stored in a directory named `.ipynb_checkpoints`. Co-authored-by: Batuhan Taskaya * Bump myst-parser from 0.18.0 to 0.18.1 in /docs (#3303) Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 0.18.0 to 0.18.1. - [Release notes](https://github.com/executablebooks/MyST-Parser/releases) - [Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/executablebooks/MyST-Parser/compare/v0.18.0...v0.18.1) --- updated-dependencies: - dependency-name: myst-parser dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump furo from 2022.9.15 to 2022.9.29 in /docs (#3304) Bumps [furo](https://github.com/pradyunsg/furo) from 2022.9.15 to 2022.9.29. - [Release notes](https://github.com/pradyunsg/furo/releases) - [Changelog](https://github.com/pradyunsg/furo/blob/main/docs/changelog.md) - [Commits](https://github.com/pradyunsg/furo/compare/2022.09.15...2022.09.29) --- updated-dependencies: - dependency-name: furo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cooper Lees * Bump sphinx from 5.2.1 to 5.2.3 in /docs (#3305) Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.2.1 to 5.2.3. - [Release notes](https://github.com/sphinx-doc/sphinx/releases) - [Changelog](https://github.com/sphinx-doc/sphinx/blob/5.x/CHANGES) - [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.2.1...v5.2.3) --- updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docutils from 0.18.1 to 0.19 in /docs (#3161) Bumps [docutils](https://docutils.sourceforge.io/) from 0.18.1 to 0.19. --- updated-dependencies: - dependency-name: docutils dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Preserve crlf line endings in blackd (#3257) Co-authored-by: KotlinIsland * Add option to skip the first line of source code (#3299) * Add option to skip the first line in source file This commit adds a CLi option to skip the first line in the source files, just like the Cpython command line allows [1]. By enabling the flag, using `-x` or `--skip-source-first-line`, the first line is removed temporarilly while the remaining contents are formatted. The first line is added back before returning the formatted output. [1]: https://docs.python.org/dev/using/cmdline.html#cmdoption-x Signed-off-by: Antonio Ossa Guerra * Add tests for `--skip-source-first-line` option When the flag is disabled (default), black formats the entire source file, as in every line. In the other hand, if the flag is enabled, by using `-x` or `--skip-source-first-line`, the first line is retained while the rest of the source is formatted and then is added back. These tests use an empty Python file that contains invalid syntax in its first line (`invalid_header.py`, at `miscellaneous/`). First, Black is invoked without enabling the flag which should result in an exit code different than 0. When the flag is enabled, Black is expected to return a successful exit code and the header is expected to be retained (even if its not valid Python syntax). Signed-off-by: Antonio Ossa Guerra * Support skip source first line option for blackd The recently added option can be added as an acceptable header for blackd. The arguments are passed in such a way that using the new header will activate the skip source first line behaviour as expected Signed-off-by: Antonio Ossa Guerra * Add skip source first line option to blackd docs The new option can be passed to blackd as a header. This commit updates the blackd docs to include the new header. Signed-off-by: Antonio Ossa Guerra * Update CHANGES.md Include the new Black option to skip the first line of source code in the configuration section Signed-off-by: Antonio Ossa Guerra * Update skip first line test including valid syntax Including valid Python syntax help us make sure that the file is still actually valid after skipping the first line of the source file (which contains invalid Python syntax) Signed-off-by: Antonio Ossa Guerra * Skip first source line at `format_file_in_place` Instead of skipping the first source line at `format_file_contents`, do it before. This allow us to find the correct newline and encoding on the actual source code (everything that's after the header). This change is also applied at Blackd: take the header before passing the source to `format_file_contents` and put the header back once we get the formatted result. Signed-off-by: Antonio Ossa Guerra * Test output newlines when skipping first line When skipping the first line of source code, the reference newline must be taken from the second line of the file instead of the first one, in case that the file mixes more than one kind of newline character Signed-off-by: Antonio Ossa Guerra * Test that Blackd also skips first line correctly Simliarly to the Black tests, we first compare that Blackd fails when the first line is invalid Python syntax and then check that the result is the expected when tha flag is activated Signed-off-by: Antonio Ossa Guerra * Use the content encoding to decode the header When decoding the header to put it back at the top of the contents of the file, use the same encoding used in the content. This should be a better "guess" that using the default value Signed-off-by: Antonio Ossa Guerra * Prepare release 22.10.0 (#3311) * Remove redundant 3.6 code and bump mypy's python_version to 3.7 (#3313) * Add support for named exprs inside function calls as gen-exps (#3327) * Fix license metadata to follow PEP 621 (#3326) * remove unreachable code (#3328) fixes #3321 * Clarify check argument is needed for github action workflow (#3325) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> * Bump sphinx from 5.2.3 to 5.3.0 in /docs (#3333) updated-dependencies: - dependency-name: sphinx dependency-type: direct:production update-type: version-update:semver-minor Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump peter-evans/find-comment from 2.0.0 to 2.0.1 (#3353) Bumps [peter-evans/find-comment](https://github.com/peter-evans/find-comment) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/peter-evans/find-comment/releases) - [Commits](https://github.com/peter-evans/find-comment/compare/1769778a0c5bd330272d749d12c036d65e70d39d...b657a70ff16d17651703a84bee1cb9ad9d2be2ea) --- updated-dependencies: - dependency-name: peter-evans/find-comment dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump peter-evans/create-or-update-comment from 2.0.0 to 2.0.1 (#3354) Bumps [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/peter-evans/create-or-update-comment/releases) - [Commits](https://github.com/peter-evans/create-or-update-comment/compare/c9fcb64660bc90ec1cc535646af190c992007c32...2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f) --- updated-dependencies: - dependency-name: peter-evans/create-or-update-comment dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Enforce empty lines before classes/functions with sticky leading comments. (#3302) Co-authored-by: Jelle Zijlstra * Exclude pytest-xdist 3.0.2 (#3356) We're getting warnings like https://github.com/psf/black/actions/runs/3325521041/jobs/5498291478 and I'm not sure how to fix them. I'll open an issue for a long-term solution, but for now avoid 3.0.2 to unbreak CI. * Update README.md (#3284) Minor typo * Wrap concatenated strings used as function args in parens (#3307) Fixes #3292 * 2space Signed-off-by: dependabot[bot] Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> Signed-off-by: Antonio Ossa Guerra Co-authored-by: Jelle Zijlstra Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> Co-authored-by: Gunung Pambudi Wibisono <55311527+gunungpw@users.noreply.github.com> Co-authored-by: Felix Hildén Co-authored-by: Josh Owen Co-authored-by: Batuhan Taskaya Co-authored-by: Shivansh-007 Co-authored-by: cbows <32486983+cbows@users.noreply.github.com> Co-authored-by: Jeffrey Lazar Co-authored-by: Jeffrey Lazar Co-authored-by: VanSHOE <75690289+VanSHOE@users.noreply.github.com> Co-authored-by: Marco Edward Gorelli Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: emfdavid <84335963+emfdavid@users.noreply.github.com> Co-authored-by: Michael Marino Co-authored-by: Rob Hammond <13874373+RHammond2@users.noreply.github.com> Co-authored-by: Perry Vargas Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Co-authored-by: Cooper Lees Co-authored-by: Nikita Sobolev Co-authored-by: Diego Co-authored-by: Nipunn Koorapati Co-authored-by: Frédérik Paradis Co-authored-by: S. Co1 Co-authored-by: Peter Mescalchin Co-authored-by: Paolo Melchiorre Co-authored-by: Joachim Jablon Co-authored-by: Xuan (Sean) Hu Co-authored-by: Laurent Lyaudet Co-authored-by: Frédérik Paradis Co-authored-by: D. Ben Knoble Co-authored-by: Tomáš Jelínek Co-authored-by: oncomouse Co-authored-by: yoerg <73831825+yoerg@users.noreply.github.com> Co-authored-by: Joseph Young <80432516+jpy-git@users.noreply.github.com> Co-authored-by: Jan-Hendrik Müller <44469195+kolibril13@users.noreply.github.com> Co-authored-by: Hugo van Kemenade Co-authored-by: Ryan Siu Co-authored-by: Sam Ezeh Co-authored-by: JiriKr <33967184+JiriKr@users.noreply.github.com> Co-authored-by: Vadim Nikolaev Co-authored-by: Cooper Lees Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com> Co-authored-by: Zac Hatfield-Dodds Co-authored-by: Iain Dorrington Co-authored-by: Sagi Shadur Co-authored-by: laundmo Co-authored-by: Yusuke Nishioka Co-authored-by: Holger Brunn Co-authored-by: Vivek Vashist Co-authored-by: Nate Prewitt Co-authored-by: Yilei "Dolee" Yang Co-authored-by: Dimitri Merejkowsky Co-authored-by: Jakub Kuczys <6032823+jack1142@users.noreply.github.com> Co-authored-by: Maciej Olko Co-authored-by: Nimrod <87605179+Panther-12@users.noreply.github.com> Co-authored-by: Cooper Ry Lees Co-authored-by: Thomas Grainger Co-authored-by: onescriptkid Co-authored-by: Yilei "Dolee" Yang Co-authored-by: Théophile Bastian Co-authored-by: Nicolò Intrieri <81313286+n-borges@users.noreply.github.com> Co-authored-by: Nicolò Co-authored-by: Tom Fryers <61272761+TomFryers@users.noreply.github.com> Co-authored-by: Alexandr Artemyev Co-authored-by: Alexander Huynh Co-authored-by: Ionite Co-authored-by: Fabio Zadrozny Co-authored-by: Martin de La Gorce Co-authored-by: James Salvatore Co-authored-by: PeterGrossmann Co-authored-by: Zsolt Dollenstein Co-authored-by: Blandes22 <96037855+Blandes22@users.noreply.github.com> Co-authored-by: Jakub Kuczys Co-authored-by: Stijn de Gooijer Co-authored-by: Ofek Lev Co-authored-by: Antonio Ossa-Guerra Co-authored-by: Ray Bell Co-authored-by: KotlinIsland <65446343+KotlinIsland@users.noreply.github.com> Co-authored-by: KotlinIsland Co-authored-by: Amethyst Reese Co-authored-by: nn <45516943+NNRepos@users.noreply.github.com> Co-authored-by: jlplenio Co-authored-by: Ned Western --- .github/workflows/diff_shades_comment.yml | 4 +- .travis.yml | 47 - AUTHORS.md | 1 + CHANGES.md | 76 +- Pipfile | 35 - Pipfile.lock | 965 ------------------ README.md | 8 +- autoload/black.vim | 4 +- docs/black_primer.md | 120 --- .../reference/reference_classes.rst | 22 +- docs/contributing_to_black.md | 70 -- docs/editor_integration.md | 288 ------ docs/faq.md | 2 +- docs/github_actions.md | 19 - docs/ignoring_unmodified_files.md | 23 - docs/index.rst | 71 -- docs/installation_and_usage.md | 179 ---- docs/integrations/github_actions.md | 4 +- docs/integrations/source_version_control.md | 2 +- docs/pyproject_toml.md | 88 -- docs/reference/reference_functions.rst | 180 ---- docs/requirements.txt | 2 +- docs/show_your_style.md | 19 - docs/the_black_code_style/future_style.md | 49 +- .../black_as_a_server.md | 3 + docs/usage_and_configuration/the_basics.md | 4 +- docs/version_control_integration.md | 28 - fuzz.py | 59 -- mypy.ini | 7 +- pyproject.toml | 2 +- setup.py | 107 -- src/black/__init__.py | 44 +- src/black/concurrency.py | 6 +- src/black/lines.py | 125 ++- src/black/mode.py | 9 +- src/black/parsing.py | 13 +- src/black/trans.py | 43 +- src/black_primer/primer.json | 121 --- src/blackd/__init__.py | 16 + src/blib2to3/Grammar.txt | 2 +- test_requirements.txt | 2 +- tests/data/comments7.py | 266 ----- tests/data/composition_no_trailing_comma.py | 367 ------- tests/data/docstring.py | 225 ---- tests/data/function_trailing_comma.py | 88 -- tests/data/long_strings_flag_disabled.py | 289 ------ tests/data/miscellaneous/invalid_header.py | 2 + tests/data/percent_precedence.py | 41 - tests/data/preview/cantfit.py | 12 +- tests/data/preview/comments9.py | 254 +++++ tests/data/preview/long_strings.py | 54 +- .../data/preview/long_strings__regression.py | 22 +- tests/data/preview/remove_await_parens.py | 1 + tests/data/py_310/pep_572_py310.py | 11 + tests/data/simple_cases/comments5.py | 6 +- ...ocstring_no_extra_empty_line_before_eof.py | 4 + tests/data/trailing_comma_optional_parens1.py | 3 - tests/data/trailing_comma_optional_parens2.py | 3 - tests/data/trailing_comma_optional_parens3.py | 8 - tests/test_black.py | 152 ++- tests/test_blackd.py | 14 + 61 files changed, 751 insertions(+), 3940 deletions(-) delete mode 100644 .travis.yml delete mode 100644 Pipfile delete mode 100644 Pipfile.lock delete mode 100644 docs/black_primer.md delete mode 100644 docs/contributing_to_black.md delete mode 100644 docs/editor_integration.md delete mode 100644 docs/github_actions.md delete mode 100644 docs/ignoring_unmodified_files.md delete mode 100644 docs/index.rst delete mode 100644 docs/installation_and_usage.md delete mode 100644 docs/pyproject_toml.md delete mode 100644 docs/reference/reference_functions.rst delete mode 100644 docs/show_your_style.md delete mode 100644 docs/version_control_integration.md delete mode 100644 fuzz.py delete mode 100644 setup.py delete mode 100644 src/black_primer/primer.json delete mode 100644 tests/data/comments7.py delete mode 100644 tests/data/composition_no_trailing_comma.py delete mode 100644 tests/data/docstring.py delete mode 100644 tests/data/function_trailing_comma.py delete mode 100644 tests/data/long_strings_flag_disabled.py create mode 100644 tests/data/miscellaneous/invalid_header.py delete mode 100644 tests/data/percent_precedence.py create mode 100644 tests/data/preview/comments9.py create mode 100644 tests/data/simple_cases/docstring_no_extra_empty_line_before_eof.py delete mode 100644 tests/data/trailing_comma_optional_parens1.py delete mode 100644 tests/data/trailing_comma_optional_parens2.py delete mode 100644 tests/data/trailing_comma_optional_parens3.py diff --git a/.github/workflows/diff_shades_comment.yml b/.github/workflows/diff_shades_comment.yml index a5d213875c7..9a22ec5f0c3 100644 --- a/.github/workflows/diff_shades_comment.yml +++ b/.github/workflows/diff_shades_comment.yml @@ -33,7 +33,7 @@ jobs: - name: Try to find pre-existing PR comment if: steps.metadata.outputs.needs-comment == 'true' id: find-comment - uses: peter-evans/find-comment@1769778a0c5bd330272d749d12c036d65e70d39d + uses: peter-evans/find-comment@b657a70ff16d17651703a84bee1cb9ad9d2be2ea with: issue-number: ${{ steps.metadata.outputs.pr-number }} comment-author: "github-actions[bot]" @@ -41,7 +41,7 @@ jobs: - name: Create or update PR comment if: steps.metadata.outputs.needs-comment == 'true' - uses: peter-evans/create-or-update-comment@c9fcb64660bc90ec1cc535646af190c992007c32 + uses: peter-evans/create-or-update-comment@2b2c85d0bf1b8a7b4e7e344bd5c71dc4b9196e9f with: comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ steps.metadata.outputs.pr-number }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 86cf24df51b..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -language: python -cache: - pip: true - directories: - - $HOME/.cache/pre-commit -env: - - TEST_CMD="coverage run -m unittest" -install: - - pip install coverage coveralls pre-commit - - pip install -e '.[d]' -script: - - $TEST_CMD -after_success: - - coveralls -notifications: - on_success: change - on_failure: always -matrix: - include: - - name: "lint" - python: 3.7 - env: - - TEST_CMD="pre-commit run --all-files --show-diff-on-failure" - - name: "3.6" - python: 3.6 - - name: "3.7" - python: 3.7 - - name: "3.8" - python: 3.8 - - name: "3.9" - python: 3.9-dev - allow_failures: - - python: 3.9-dev -before_deploy: - - pip install pyinstaller - - pyinstaller --clean -F --add-data src/blib2to3/:blib2to3 src/black/__init__.py -deploy: - provider: releases - api_key: - secure: chYvcmnRqRKtfBcAZRj62rEv0ziWuHMl6MnfQbd1MOVQ4njntI8+CCPk118dW6MWSfwTqyMFy+t9gAgQYhjkLEHMS2aK9Z2wCWki1MkBrkMw5tYoLFvPu0KQ9rIVihxsr93a/am6Oh/Hp+1uuc4zWPUf1ubX+QlCzsxjCzVso1kTJjjdN04UxvkcFR+sY2d9Qyy9WcdifChnLwdmIJKIoVOE7Imm820nzImJHkJh8iSnjBjL98gvPPeC/nWTltsbErvf2mCv4NIjzjQZvHa87c7rSJGbliNrAxCSyyvBX+JNeS8U2fGLE83do0HieyjdPbTuc27e2nsrrihgPh+hXbiJerljclfp5hsJ5qGz5sS9MU1fR7sSLiQQ2v0TYB5RRwd34TgGiLwFAZZmgZOfMUCtefCKvP8qvELMSNd99+msfPEHiuhADF0bKPTbCUa6BgUHNr6woOLmHerjPHd6NI/a8Skz/uQB4xr3spLSmfUmX0fEqyYUDphkGPNH8IsvC1/F2isecW9kOzEWmB5oCmpMTGm4TIf3C01Nx+9PVwB2Z+30hhbfIEBxD4loRFmh/hU5TIQEpneF8yoIfe9EnMaoZbq86xhADZXvLIZvpXUdm1NQZDG6na2S1fwyOUKQsW6BWLcfoZZwZlrXrViD1jBsHBV++s+lxShTeTCszlo= - file: - - dist/black - skip_cleanup: true - on: - condition: $TRAVIS_PYTHON_VERSION == '3.6' - repo: psf/black - tags: true diff --git a/AUTHORS.md b/AUTHORS.md index f2d599dd878..a635e8c3c92 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -24,6 +24,7 @@ Multiple contributions by: - [Alex Vandiver](mailto:github@chmrr.net) - [Allan Simon](mailto:allan.simon@supinfo.com) - Anders-Petter Ljungquist +- [Amethyst Reese](mailto:amy@n7.gg) - [Andrew Thorp](mailto:andrew.thorp.dev@gmail.com) - [Andrew Zhou](mailto:andrewfzhou@gmail.com) - [Andrey](mailto:dyuuus@yandex.ru) diff --git a/CHANGES.md b/CHANGES.md index 4ff181674b1..4db961149bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,44 +6,34 @@ -- Runtime support for Python 3.6 has been removed. Formatting 3.6 code will still be - supported until further notice. - ### Stable style -- Fix a crash when `# fmt: on` is used on a different block level than `# fmt: off` - (#3281) - ### Preview style -- Fix a crash when formatting some dicts with parenthesis-wrapped long string keys - (#3262) +- Enforce empty lines before classes and functions with sticky leading comments (#3302) +- Implicitly concatenated strings used as function args are now wrapped inside + parentheses (#3307) ### Configuration -- `.ipynb_checkpoints` directories are now excluded by default (#3293) - ### Packaging -- Executables made with PyInstaller will no longer crash when formatting several files - at once on macOS. Native x86-64 executables for macOS are available once again. - (#3275) -- Hatchling is now used as the build backend. This will not have any effect for users - who install Black with its wheels from PyPI. (#3233) -- Faster compiled wheels are now available for CPython 3.11 (#3276) - ### Parser +- Parsing support has been added for walruses inside generator expression that are + passed as function args (for example, + `any(match := my_re.match(text) for text in texts)`) (#3327). + ### Performance @@ -54,22 +44,61 @@ ### _Blackd_ -- Windows style (CRLF) newlines will be preserved (#3257). + ### Integrations -- Update GitHub Action to support formatting of Jupyter Notebook files via a `jupyter` - option (#3282) -- Update GitHub Action to support use of version specifiers (e.g. `<23`) for Black - version (#3265) - ### Documentation +## 22.10.0 + +### Highlights + +- Runtime support for Python 3.6 has been removed. Formatting 3.6 code will still be + supported until further notice. + +### Stable style + +- Fix a crash when `# fmt: on` is used on a different block level than `# fmt: off` + (#3281) + +### Preview style + +- Fix a crash when formatting some dicts with parenthesis-wrapped long string keys + (#3262) + +### Configuration + +- `.ipynb_checkpoints` directories are now excluded by default (#3293) +- Add `--skip-source-first-line` / `-x` option to ignore the first line of source code + while formatting (#3299) + +### Packaging + +- Executables made with PyInstaller will no longer crash when formatting several files + at once on macOS. Native x86-64 executables for macOS are available once again. + (#3275) +- Hatchling is now used as the build backend. This will not have any effect for users + who install Black with its wheels from PyPI. (#3233) +- Faster compiled wheels are now available for CPython 3.11 (#3276) + +### _Blackd_ + +- Windows style (CRLF) newlines will be preserved (#3257). + +### Integrations + +- Vim plugin: add flag (`g:black_preview`) to enable/disable the preview style (#3246) +- Update GitHub Action to support formatting of Jupyter Notebook files via a `jupyter` + option (#3282) +- Update GitHub Action to support use of version specifiers (e.g. `<23`) for Black + version (#3265) + ## 22.8.0 ### Highlights @@ -124,7 +153,6 @@ - Vim plugin: prefix messages with `Black: ` so it's clear they come from Black (#3194) - Docker: changed to a /opt/venv installation + added to PATH to be available to non-root users (#3202) -- Vim plugin: add flag (`g:black_preview`) to enable/disable the preview style (#3246) ### Output diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 44f57f6773c..00000000000 --- a/Pipfile +++ /dev/null @@ -1,35 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.python.org/simple" -verify_ssl = true - -[dev-packages] -Sphinx = ">=3.1.2" -coverage = "*" -docutils = "==0.15" # not a direct dependency, see https://github.com/pypa/pipenv/issues/3865 -flake8 = "*" -flake8-bugbear = "*" -flake8-mypy = "*" -mypy = ">=0.782" -pre-commit = "*" -readme_renderer = "*" -recommonmark = "*" -setuptools = ">=39.2.0" -setuptools-scm = "*" -twine = ">=1.11.0" -wheel = ">=0.31.1" -black = {editable = true, extras = ["d"], path = "."} - -[packages] -aiohttp = ">=3.3.2" -aiohttp-cors = "*" -appdirs = "*" -click = ">=7.0" -mypy_extensions = ">=0.4.3" -pathspec = ">=0.6" -regex = ">=2020.1.8" -toml = ">=0.10.1" -typed-ast = "==1.4.0" -typing_extensions = ">=3.7.4" -black = {editable = true,extras = ["d"],path = "."} -dataclasses = {"python_version <" = "3.7","version >" = "0.6"} diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 32b8012ff0e..00000000000 --- a/Pipfile.lock +++ /dev/null @@ -1,965 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "61d09a6b8a8c310becd5e108ed08e0eeae50c7323c08c8040367abded0cb1031" - }, - "pipfile-spec": 6, - "requires": {}, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.python.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "aiohttp": { - "hashes": [ - "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e", - "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326", - "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a", - "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654", - "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a", - "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4", - "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17", - "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec", - "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd", - "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48", - "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59", - "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965" - ], - "index": "pypi", - "version": "==3.6.2" - }, - "aiohttp-cors": { - "hashes": [ - "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e", - "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d" - ], - "index": "pypi", - "version": "==0.7.0" - }, - "appdirs": { - "hashes": [ - "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", - "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" - ], - "index": "pypi", - "version": "==1.4.4" - }, - "async-timeout": { - "hashes": [ - "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", - "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" - ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.0.1" - }, - "attrs": { - "hashes": [ - "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a", - "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.1.0" - }, - "black": { - "editable": true, - "extras": [ - "d" - ], - "path": "." - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "click": { - "hashes": [ - "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", - "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" - ], - "index": "pypi", - "version": "==7.1.2" - }, - "dataclasses": { - "hashes": [ - "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f", - "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84" - ], - "index": "pypi", - "python_version <": "3.7", - "version": "==0.6", - "version >": "0.6" - }, - "idna": { - "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.10" - }, - "multidict": { - "hashes": [ - "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", - "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", - "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", - "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", - "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", - "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", - "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", - "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", - "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", - "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", - "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", - "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", - "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", - "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", - "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", - "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", - "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" - ], - "markers": "python_version >= '3.5'", - "version": "==4.7.6" - }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "index": "pypi", - "version": "==0.4.3" - }, - "pathspec": { - "hashes": [ - "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0", - "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061" - ], - "index": "pypi", - "version": "==0.8.0" - }, - "regex": { - "hashes": [ - "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204", - "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162", - "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f", - "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb", - "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6", - "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7", - "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88", - "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99", - "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644", - "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a", - "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840", - "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067", - "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd", - "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4", - "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e", - "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89", - "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e", - "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc", - "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf", - "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341", - "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7" - ], - "index": "pypi", - "version": "==2020.7.14" - }, - "setuptools-scm": { - "hashes": [ - "sha256:09c659d1d6680811c43f476a33c6d3d9872416580786e96bd29ea03e6a818e41", - "sha256:69258e2eeba5f7ce1ed7a5f109519580fa3578250f8e4d6684859f86d1b15826", - "sha256:731550a27e3901ee501c3bf99140b5436b8eeba341a9d19911cf364b8d573293", - "sha256:892e63b4983f9e30f9e8bf89ad03d2a02a47e6e5ced09b03ae6fe952ade8a579", - "sha256:a8994582e716ec690f33fec70cca0f85bd23ec974e3f783233e4879090a7faa8", - "sha256:b42c150c34d6120babf3646abd7513e032be2e230b3d2034f27404c65aa0c977", - "sha256:eaaec16b7af25c5f532b5af2332213bb6223d15cca4518f6dbc4c055641c86fd", - "sha256:efc928d6a64162c88cdc85fa4b84adfbd6dbf9f9b04319bc495eb16dcfaae00a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==4.1.2" - }, - "toml": { - "hashes": [ - "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", - "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", - "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", - "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" - ], - "index": "pypi", - "version": "==0.10.1" - }, - "typed-ast": { - "hashes": [ - "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", - "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", - "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", - "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", - "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", - "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", - "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", - "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", - "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", - "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", - "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", - "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", - "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", - "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", - "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", - "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", - "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", - "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", - "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", - "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" - ], - "index": "pypi", - "version": "==1.4.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", - "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", - "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" - ], - "index": "pypi", - "version": "==3.7.4.3" - }, - "yarl": { - "hashes": [ - "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409", - "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593", - "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2", - "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8", - "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d", - "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692", - "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02", - "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a", - "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8", - "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6", - "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511", - "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e", - "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a", - "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb", - "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f", - "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317", - "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6" - ], - "markers": "python_version >= '3.5'", - "version": "==1.5.1" - } - }, - "develop": { - "aiohttp": { - "hashes": [ - "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e", - "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326", - "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a", - "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654", - "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a", - "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4", - "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17", - "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec", - "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd", - "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48", - "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59", - "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965" - ], - "index": "pypi", - "version": "==3.6.2" - }, - "aiohttp-cors": { - "hashes": [ - "sha256:0451ba59fdf6909d0e2cd21e4c0a43752bc0703d33fc78ae94d9d9321710193e", - "sha256:4d39c6d7100fd9764ed1caf8cebf0eb01bf5e3f24e2e073fda6234bc48b19f5d" - ], - "index": "pypi", - "version": "==0.7.0" - }, - "alabaster": { - "hashes": [ - "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", - "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02" - ], - "version": "==0.7.12" - }, - "appdirs": { - "hashes": [ - "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", - "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128" - ], - "index": "pypi", - "version": "==1.4.4" - }, - "async-timeout": { - "hashes": [ - "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", - "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" - ], - "markers": "python_full_version >= '3.5.3'", - "version": "==3.0.1" - }, - "attrs": { - "hashes": [ - "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a", - "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.1.0" - }, - "babel": { - "hashes": [ - "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38", - "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.0" - }, - "black": { - "editable": true, - "extras": [ - "d" - ], - "path": "." - }, - "bleach": { - "hashes": [ - "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f", - "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==3.1.5" - }, - "certifi": { - "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" - ], - "version": "==2020.6.20" - }, - "cfgv": { - "hashes": [ - "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d", - "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.2.0" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "click": { - "hashes": [ - "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", - "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" - ], - "index": "pypi", - "version": "==7.1.2" - }, - "colorama": { - "hashes": [ - "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", - "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==0.4.3" - }, - "commonmark": { - "hashes": [ - "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60", - "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9" - ], - "version": "==0.9.1" - }, - "coverage": { - "hashes": [ - "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb", - "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3", - "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716", - "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034", - "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3", - "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8", - "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0", - "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f", - "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4", - "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962", - "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d", - "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b", - "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4", - "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3", - "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258", - "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59", - "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01", - "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd", - "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b", - "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d", - "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89", - "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd", - "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b", - "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d", - "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46", - "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546", - "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082", - "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b", - "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4", - "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8", - "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811", - "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd", - "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651", - "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0" - ], - "index": "pypi", - "version": "==5.2.1" - }, - "distlib": { - "hashes": [ - "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb", - "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1" - ], - "version": "==0.3.1" - }, - "docutils": { - "hashes": [ - "sha256:54a349c622ff31c91cbec43b0b512f113b5b24daf00e2ea530bb1bd9aac14849", - "sha256:ba4584f9107571ced0d2c7f56a5499c696215ba90797849c92d395979da68521", - "sha256:d2ddba74835cb090a1b627d3de4e7835c628d07ee461f7b4480f51af2fe4d448" - ], - "index": "pypi", - "version": "==0.15" - }, - "filelock": { - "hashes": [ - "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", - "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" - ], - "version": "==3.0.12" - }, - "flake8": { - "hashes": [ - "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c", - "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208" - ], - "index": "pypi", - "version": "==3.8.3" - }, - "flake8-bugbear": { - "hashes": [ - "sha256:a3ddc03ec28ba2296fc6f89444d1c946a6b76460f859795b35b77d4920a51b63", - "sha256:bd02e4b009fb153fe6072c31c52aeab5b133d508095befb2ffcf3b41c4823162" - ], - "index": "pypi", - "version": "==20.1.4" - }, - "flake8-mypy": { - "hashes": [ - "sha256:47120db63aff631ee1f84bac6fe8e64731dc66da3efc1c51f85e15ade4a3ba18", - "sha256:cff009f4250e8391bf48990093cff85802778c345c8449d6498b62efefeebcbc" - ], - "index": "pypi", - "version": "==17.8.0" - }, - "identify": { - "hashes": [ - "sha256:9f5fcf22b665eaece583bd395b103c2769772a0f646ffabb5b1f155901b07de2", - "sha256:b1aa2e05863dc80242610d46a7b49105e2eafe00ef0c8ff311c1828680760c76" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.4.29" - }, - "idna": { - "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.10" - }, - "imagesize": { - "hashes": [ - "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1", - "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.0" - }, - "jinja2": { - "hashes": [ - "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", - "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.11.2" - }, - "keyring": { - "hashes": [ - "sha256:182f94fc0381546489e3e4d90384a8c1d43cc09ffe2eb4a826e7312df6e1be7c", - "sha256:cd4d486803d55bdb13e2d453eb61dbbc984773e4f2b98a455aa85b1f4bc421e4" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3.1" - }, - "markupsafe": { - "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.1.1" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "multidict": { - "hashes": [ - "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", - "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", - "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", - "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", - "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", - "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", - "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", - "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", - "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", - "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", - "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", - "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", - "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", - "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", - "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", - "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", - "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" - ], - "markers": "python_version >= '3.5'", - "version": "==4.7.6" - }, - "mypy": { - "hashes": [ - "sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c", - "sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86", - "sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b", - "sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd", - "sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc", - "sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea", - "sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e", - "sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308", - "sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406", - "sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d", - "sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707", - "sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d", - "sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c", - "sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a" - ], - "index": "pypi", - "version": "==0.782" - }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "index": "pypi", - "version": "==0.4.3" - }, - "nodeenv": { - "hashes": [ - "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9", - "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c" - ], - "version": "==1.5.0" - }, - "packaging": { - "hashes": [ - "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", - "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.4" - }, - "pathspec": { - "hashes": [ - "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0", - "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061" - ], - "index": "pypi", - "version": "==0.8.0" - }, - "pkginfo": { - "hashes": [ - "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb", - "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32" - ], - "version": "==1.5.0.1" - }, - "pre-commit": { - "hashes": [ - "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a", - "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70" - ], - "index": "pypi", - "version": "==2.7.1" - }, - "pycodestyle": { - "hashes": [ - "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", - "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.6.0" - }, - "pyflakes": { - "hashes": [ - "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", - "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.2.0" - }, - "pygments": { - "hashes": [ - "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", - "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" - ], - "markers": "python_version >= '3.5'", - "version": "==2.6.1" - }, - "pyparsing": { - "hashes": [ - "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", - "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.4.7" - }, - "pytz": { - "hashes": [ - "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed", - "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048" - ], - "version": "==2020.1" - }, - "pyyaml": { - "hashes": [ - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" - ], - "version": "==5.3.1" - }, - "readme-renderer": { - "hashes": [ - "sha256:cbe9db71defedd2428a1589cdc545f9bd98e59297449f69d721ef8f1cfced68d", - "sha256:cc4957a803106e820d05d14f71033092537a22daa4f406dfbdd61177e0936376" - ], - "index": "pypi", - "version": "==26.0" - }, - "recommonmark": { - "hashes": [ - "sha256:29cd4faeb6c5268c633634f2d69aef9431e0f4d347f90659fd0aab20e541efeb", - "sha256:2ec4207a574289355d5b6ae4ae4abb29043346ca12cdd5f07d374dc5987d2852" - ], - "index": "pypi", - "version": "==0.6.0" - }, - "regex": { - "hashes": [ - "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204", - "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162", - "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f", - "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb", - "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6", - "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7", - "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88", - "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99", - "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644", - "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a", - "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840", - "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067", - "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd", - "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4", - "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e", - "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89", - "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e", - "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc", - "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf", - "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341", - "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7" - ], - "index": "pypi", - "version": "==2020.7.14" - }, - "requests": { - "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.24.0" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f", - "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" - ], - "version": "==0.9.1" - }, - "rfc3986": { - "hashes": [ - "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d", - "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50" - ], - "version": "==1.4.0" - }, - "setuptools-scm": { - "hashes": [ - "sha256:09c659d1d6680811c43f476a33c6d3d9872416580786e96bd29ea03e6a818e41", - "sha256:69258e2eeba5f7ce1ed7a5f109519580fa3578250f8e4d6684859f86d1b15826", - "sha256:731550a27e3901ee501c3bf99140b5436b8eeba341a9d19911cf364b8d573293", - "sha256:892e63b4983f9e30f9e8bf89ad03d2a02a47e6e5ced09b03ae6fe952ade8a579", - "sha256:a8994582e716ec690f33fec70cca0f85bd23ec974e3f783233e4879090a7faa8", - "sha256:b42c150c34d6120babf3646abd7513e032be2e230b3d2034f27404c65aa0c977", - "sha256:eaaec16b7af25c5f532b5af2332213bb6223d15cca4518f6dbc4c055641c86fd", - "sha256:efc928d6a64162c88cdc85fa4b84adfbd6dbf9f9b04319bc495eb16dcfaae00a" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==4.1.2" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.15.0" - }, - "snowballstemmer": { - "hashes": [ - "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", - "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52" - ], - "version": "==2.0.0" - }, - "sphinx": { - "hashes": [ - "sha256:321d6d9b16fa381a5306e5a0b76cd48ffbc588e6340059a729c6fdd66087e0e8", - "sha256:ce6fd7ff5b215af39e2fcd44d4a321f6694b4530b6f2b2109b64d120773faea0" - ], - "index": "pypi", - "version": "==3.2.1" - }, - "sphinxcontrib-applehelp": { - "hashes": [ - "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a", - "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.2" - }, - "sphinxcontrib-devhelp": { - "hashes": [ - "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e", - "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.2" - }, - "sphinxcontrib-htmlhelp": { - "hashes": [ - "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f", - "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.3" - }, - "sphinxcontrib-jsmath": { - "hashes": [ - "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", - "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.1" - }, - "sphinxcontrib-qthelp": { - "hashes": [ - "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", - "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.3" - }, - "sphinxcontrib-serializinghtml": { - "hashes": [ - "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", - "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" - ], - "markers": "python_version >= '3.5'", - "version": "==1.1.4" - }, - "toml": { - "hashes": [ - "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", - "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", - "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", - "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" - ], - "index": "pypi", - "version": "==0.10.1" - }, - "tqdm": { - "hashes": [ - "sha256:1a336d2b829be50e46b84668691e0a2719f26c97c62846298dd5ae2937e4d5cf", - "sha256:564d632ea2b9cb52979f7956e093e831c28d441c11751682f84c86fc46e4fd21" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.48.2" - }, - "twine": { - "hashes": [ - "sha256:34352fd52ec3b9d29837e6072d5a2a7c6fe4290e97bba46bb8d478b5c598f7ab", - "sha256:ba9ff477b8d6de0c89dd450e70b2185da190514e91c42cc62f96850025c10472" - ], - "index": "pypi", - "version": "==3.2.0" - }, - "typed-ast": { - "hashes": [ - "sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", - "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", - "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", - "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", - "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", - "sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", - "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", - "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", - "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", - "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", - "sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", - "sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", - "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", - "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", - "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", - "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", - "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", - "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", - "sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", - "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" - ], - "index": "pypi", - "version": "==1.4.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", - "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", - "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" - ], - "index": "pypi", - "version": "==3.7.4.3" - }, - "urllib3": { - "hashes": [ - "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", - "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", - "version": "==1.25.10" - }, - "virtualenv": { - "hashes": [ - "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc", - "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.0.31" - }, - "webencodings": { - "hashes": [ - "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", - "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" - ], - "version": "==0.5.1" - }, - "wheel": { - "hashes": [ - "sha256:497add53525d16c173c2c1c733b8f655510e909ea78cc0e29d374243544b77a2", - "sha256:99a22d87add3f634ff917310a3d87e499f19e663413a52eb9232c447aa646c9f" - ], - "index": "pypi", - "version": "==0.35.1" - }, - "yarl": { - "hashes": [ - "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409", - "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593", - "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2", - "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8", - "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d", - "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692", - "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02", - "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a", - "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8", - "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6", - "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511", - "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e", - "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a", - "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb", - "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f", - "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317", - "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6" - ], - "markers": "python_version >= '3.5'", - "version": "==1.5.1" - } - } -} diff --git a/README.md b/README.md index 0ab059185f7..6b72a9b9a23 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,6 @@ If you can't wait for the latest _hotness_ and want to install from GitHub, use: `pip install git+https://github.com/psf/black` -#### Install from GitHub - -If you can't wait for the latest _hotness_ and want to install from GitHub, use: - -`pip install git+git://github.com/psf/black` - ### Usage To get started right away with sensible defaults: @@ -72,7 +66,7 @@ Further information can be found in our docs: _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, and our own auto formatting and parallel Continuous Integration runner. Now that -we have become stable, you should not expect large formatting to changes in the future. +we have become stable, you should not expect large formatting changes in the future. Stylistic changes will mostly be responses to bug reports and support for new Python syntax. For more information please refer to the [The Black Code Style](https://black.readthedocs.io/en/stable/the_black_code_style/index.html). diff --git a/autoload/black.vim b/autoload/black.vim index e87a1e4edfa..eec44637950 100644 --- a/autoload/black.vim +++ b/autoload/black.vim @@ -57,8 +57,8 @@ def _get_virtualenv_site_packages(venv_path, pyver): def _initialize_black_env(upgrade=False): pyver = sys.version_info[:3] - if pyver < (3, 6, 2): - print("Sorry, Black requires Python 3.6.2+ to run.") + if pyver < (3, 7): + print("Sorry, Black requires Python 3.7+ to run.") return False from pathlib import Path diff --git a/docs/black_primer.md b/docs/black_primer.md deleted file mode 100644 index a2dd964b7dc..00000000000 --- a/docs/black_primer.md +++ /dev/null @@ -1,120 +0,0 @@ -# black-primer - -`black-primer` is a tool built for CI (and humans) to have _Black_ `--check` a number of -(configured in `primer.json`) Git accessible projects in parallel. _(A PR will be -accepted to add Mercurial support.)_ - -## Run flow - -- Ensure we have a `black` + `git` in PATH -- Load projects from `primer.json` -- Run projects in parallel with `--worker` workers (defaults to CPU count / 2) - - Checkout projects - - Run black and record result - - Clean up repository checkout _(can optionally be disabled via `--keep`)_ -- Display results summary to screen -- Default to cleaning up `--work-dir` (which defaults to tempfile schemantics) -- Return - - 0 for successful run - - < 0 for environment / internal error - - \> 0 for each project with an error - -## Speed up runs 🏎 - -If you're running locally yourself to test black on lots of code try: - -- Using `-k` / `--keep` + `-w` / `--work-dir` so you don't have to re-checkout the repo - each run - -## CLI arguments - -```text -Usage: black-primer [OPTIONS] - - primer - prime projects for blackening... 🏴 - -Options: - -c, --config PATH JSON config file path [default: /Users/cooper/repos/ - black/src/black_primer/primer.json] - - --debug Turn on debug logging [default: False] - -k, --keep Keep workdir + repos post run [default: False] - -L, --long-checkouts Pull big projects to test [default: False] - -R, --rebase Rebase project if already checked out [default: - False] - - -w, --workdir PATH Directory path for repo checkouts [default: /var/fol - ders/tc/hbwxh76j1hn6gqjd2n2sjn4j9k1glp/T/primer.20200 - 517125229] - - -W, --workers INTEGER Number of parallel worker coroutines [default: 2] - -h, --help Show this message and exit. -``` - -## Primer config file - -The config file is in JSON format. Its main element is the `"projects"` dictionary and -each parameter is explained below: - -```json -{ - "projects": { - "00_Example": { - "cli_arguments": "List of extra CLI arguments to pass Black for this project", - "expect_formatting_changes": "Boolean to indicate that the version of Black is expected to cause changes", - "git_clone_url": "URL you would pass `git clone` to check out this repo", - "long_checkout": "Boolean to have repo skipped by default unless `--long-checkouts` is specified", - "py_versions": "List of major Python versions to run this project with - all will do as you'd expect - run on ALL versions" - }, - "aioexabgp": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/cooperlees/aioexabgp.git", - "long_checkout": false, - "py_versions": ["all", "3.8"] - } - } -} -``` - -An example primer config file is used by Black -[here](https://github.com/psf/black/blob/master/src/black_primer/primer.json) - -## Example run - -```console -cooper-mbp:black cooper$ ~/venvs/b/bin/black-primer -[2020-05-17 13:06:40,830] INFO: 4 projects to run Black over (lib.py:270) -[2020-05-17 13:06:44,215] INFO: Analyzing results (lib.py:285) --- primer results 📊 -- - -3 / 4 succeeded (75.0%) ✅ -1 / 4 FAILED (25.0%) 💩 - - 0 projects disabled by config - - 0 projects skipped due to Python version - - 0 skipped due to long checkout - -Failed projects: - -## flake8-bugbear: - - Returned 1 - - stdout: ---- tests/b303_b304.py 2020-05-17 20:04:09.991227 +0000 -+++ tests/b303_b304.py 2020-05-17 20:06:42.753851 +0000 -@@ -26,11 +26,11 @@ - maxint = 5 # this is okay - # the following should not crash - (a, b, c) = list(range(3)) - # it is different than this - a, b, c = list(range(3)) -- a, b, c, = list(range(3)) -+ a, b, c = list(range(3)) - # and different than this - (a, b), c = list(range(3)) - a, *b, c = [1, 2, 3, 4, 5] - b[1:3] = [0, 0] - -would reformat tests/b303_b304.py -Oh no! 💥 💔 💥 -1 file would be reformatted, 22 files would be left unchanged. -``` diff --git a/docs/contributing/reference/reference_classes.rst b/docs/contributing/reference/reference_classes.rst index fa765961e69..3931e0e0072 100644 --- a/docs/contributing/reference/reference_classes.rst +++ b/docs/contributing/reference/reference_classes.rst @@ -11,23 +11,29 @@ .. autoclass:: black.brackets.BracketTracker :members: -:class:`EmptyLineTracker` +:class:`Line` +------------- + +.. autoclass:: black.lines.Line + :members: + :special-members: __str__, __bool__ + +:class:`LinesBlock` ------------------------- -.. autoclass:: black.EmptyLineTracker +.. autoclass:: black.lines.LinesBlock :members: -:class:`Line` -------------- +:class:`EmptyLineTracker` +------------------------- -.. autoclass:: black.Line +.. autoclass:: black.lines.EmptyLineTracker :members: - :special-members: __str__, __bool__ :class:`LineGenerator` ---------------------- -.. autoclass:: black.LineGenerator +.. autoclass:: black.linegen.LineGenerator :show-inheritance: :members: @@ -40,7 +46,7 @@ :class:`Report` --------------- -.. autoclass:: black.Report +.. autoclass:: black.report.Report :members: :special-members: __str__ diff --git a/docs/contributing_to_black.md b/docs/contributing_to_black.md deleted file mode 100644 index e5307adb5d0..00000000000 --- a/docs/contributing_to_black.md +++ /dev/null @@ -1,70 +0,0 @@ -[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM CONTRIBUTING.md" - -# Contributing to _Black_ - -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? - -## Bird's eye view - -In terms of inspiration, _Black_ is about as configurable as _gofmt_. This is -deliberate. - -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. - -## Technicalities - -Development on the latest version of Python is preferred. As of this writing it's 3.8. -You can use any operating system. I am using macOS myself and CentOS at work. - -Install all development dependencies using: - -```console -$ pipenv install --dev -$ pipenv shell -$ pre-commit install -``` - -If you haven't used `pipenv` before but are comfortable with virtualenvs, just run -`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. - -Before submitting pull requests, run lints and tests with: - -```console -$ pre-commit run -a -$ python -m unittest -$ black-primer [-k -w /tmp/black_test_repos] -``` - -## 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/master/src/black_primer/primer.json) -configuration for it to pass. - -For more `black-primer` information visit the -[documentation](https://github.com/psf/black/blob/master/docs/black_primer.md). - -## Hygiene - -If you're fixing a bug, add a test. Run it first to confirm it fails, then fix the bug, -run it again to confirm it's really fixed. - -If adding a new feature, add a test. In fact, always add a test. But wait, before adding -any large feature, first open an issue for us to discuss the idea first. - -## Finally - -Thanks again for your interest in improving the project! You're taking action when most -people decide to sit and watch. diff --git a/docs/editor_integration.md b/docs/editor_integration.md deleted file mode 100644 index 73107d6a4a1..00000000000 --- a/docs/editor_integration.md +++ /dev/null @@ -1,288 +0,0 @@ -# Editor integration - -## Emacs - -Options include the following: - -- [purcell/reformatter.el](https://github.com/purcell/reformatter.el) -- [proofit404/blacken](https://github.com/pythonic-emacs/blacken) -- [Elpy](https://github.com/jorgenschaefer/elpy). - -## PyCharm/IntelliJ IDEA - -1. Install `black`. - -```console -$ pip install black -``` - -2. 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`. - -3. Open External tools in PyCharm/IntelliJ IDEA - -On macOS: - -`PyCharm -> Preferences -> Tools -> External Tools` - -On Windows / Linux / BSD: - -`File -> Settings -> Tools -> External Tools` - -4. Click the + icon to add a new external tool with the following values: - - - Name: Black - - Description: Black is the uncompromising Python code formatter. - - Program: - - Arguments: `"$FilePath$"` - -5. Format the currently opened file by selecting `Tools -> External Tools -> black`. - - - Alternatively, you can set a keyboard shortcut by navigating to - `Preferences or Settings -> Keymap -> External Tools -> External Tools - Black`. - -6. Optionally, run _Black_ on every file save: - - 1. Make sure you have the - [File Watchers](https://plugins.jetbrains.com/plugin/7177-file-watchers) plugin - installed. - 2. Go to `Preferences or Settings -> Tools -> File Watchers` and click `+` to add a - new watcher: - - Name: Black - - File type: Python - - Scope: Project Files - - Program: - - Arguments: `$FilePath$` - - Output paths to refresh: `$FilePath$` - - Working directory: `$ProjectFileDir$` - - - Uncheck "Auto-save edited files to trigger the watcher" in Advanced Options - -## Wing IDE - -Wing supports black via the OS Commands tool, as explained in the Wing documentation on -[pep8 formatting](https://wingware.com/doc/edit/pep8). The detailed procedure is: - -1. Install `black`. - -```console -$ pip install black -``` - -2. Make sure it runs from the command line, e.g. - -```console -$ black --help -``` - -3. In Wing IDE, activate the **OS Commands** panel and define the command **black** to - execute black on the currently selected file: - -- Use the Tools -> OS Commands menu selection -- 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 - -4. Select a file in the editor and press **F1** , or whatever key binding you selected - in step 3, to reformat the file. - -## Vim - -Commands and shortcuts: - -- `:Black` to format the entire file (ranges not supported); -- `:BlackUpgrade` to upgrade _Black_ inside the virtualenv; -- `:BlackVersion` to get the current version of _Black_ inside the virtualenv. - -Configuration: - -- `g:black_fast` (defaults to `0`) -- `g:black_linelength` (defaults to `88`) -- `g:black_skip_string_normalization` (defaults to `0`) -- `g:black_virtualenv` (defaults to `~/.vim/black` or `~/.local/share/nvim/black`) - -To install with [vim-plug](https://github.com/junegunn/vim-plug): - -``` -Plug 'psf/black', { 'branch': 'stable' } -``` - -or with [Vundle](https://github.com/VundleVim/Vundle.vim): - -``` -Plugin 'psf/black' -``` - -and execute the following in a terminal: - -```console -$ cd ~/.vim/bundle/black -$ git checkout origin/stable -b stable -``` - -or you can copy the plugin from -[plugin/black.vim](https://github.com/psf/black/blob/stable/plugin/black.vim). - -``` -mkdir -p ~/.vim/pack/python/start/black/plugin -curl https://raw.githubusercontent.com/psf/black/stable/plugin/black.vim -o ~/.vim/pack/python/start/black/plugin/black.vim -``` - -Let me know if this requires any changes to work with Vim 8's builtin `packadd`, or -Pathogen, and so on. - -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 -automatically installs _Black_. You can upgrade it later by calling `:BlackUpgrade` and -restarting Vim. - -If you need to do anything special to make your virtualenv work and install _Black_ (for -example you want to run a version from master), create a virtualenv manually and point -`g:black_virtualenv` to it. The plugin will use it. - -To run _Black_ on save, add the following line to `.vimrc` or `init.vim`: - -``` -autocmd BufWritePre *.py execute ':Black' -``` - -To run _Black_ on a key press (e.g. F9 below), add this: - -``` -nnoremap :Black -``` - -**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 "", line 63, in - File "/home/gui/.vim/black/lib/python3.7/site-packages/black.py", line 45, in - from typed_ast import ast3, ast27 - File "/home/gui/.vim/black/lib/python3.7/site-packages/typed_ast/ast3.py", line 40, in - 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 -``` - -## Visual Studio Code - -Use the -[Python extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) -([instructions](https://code.visualstudio.com/docs/python/editing#_formatting)). - -## SublimeText 3 - -Use [sublack plugin](https://github.com/jgirardet/sublack). - -## Jupyter Notebook Magic - -Use [blackcellmagic](https://github.com/csurfer/blackcellmagic). - -## Python Language Server - -If your editor supports the [Language Server Protocol](https://langserver.org/) (Atom, -Sublime Text, Visual Studio Code and many more), you can use the -[Python Language Server](https://github.com/palantir/python-language-server) with the -[pyls-black](https://github.com/rupert/pyls-black) plugin. - -## Atom/Nuclide - -Use [python-black](https://atom.io/packages/python-black). - -## Gradle (the build tool) - -Use the [Spotless](https://github.com/diffplug/spotless/tree/main/plugin-gradle) plugin. - -## Kakoune - -Add the following hook to your kakrc, then run _Black_ with `:format`. - -``` -hook global WinSetOption filetype=python %{ - set-option window formatcmd 'black -q -' -} -``` - -## Thonny - -Use [Thonny-black-code-format](https://github.com/Franccisco/thonny-black-code-format). - -## Other integrations - -Other editors and tools will require external contributions. - -Patches welcome! ✨ 🍰 ✨ - -Any tool that 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). diff --git a/docs/faq.md b/docs/faq.md index 8b9ffb0202e..bc9deccb756 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -95,7 +95,7 @@ Support for formatting Python 2 code was removed in version 22.0. While we've ma 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. -Runtime support for 3.6 was removed in version 22.9.0. +Runtime support for 3.6 was removed in version 22.10.0. ## Why does my linter or typechecker complain after I format my code? diff --git a/docs/github_actions.md b/docs/github_actions.md deleted file mode 100644 index 7ff87540242..00000000000 --- a/docs/github_actions.md +++ /dev/null @@ -1,19 +0,0 @@ -[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md" - -# GitHub Actions - -Create a file named `.github/workflows/black.yml` inside your repository with: - -```yaml -name: Lint - -on: [push, pull_request] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: psf/black@stable -``` diff --git a/docs/ignoring_unmodified_files.md b/docs/ignoring_unmodified_files.md deleted file mode 100644 index a915f4e8678..00000000000 --- a/docs/ignoring_unmodified_files.md +++ /dev/null @@ -1,23 +0,0 @@ -[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md" - -# Ignoring unmodified files - -_Black_ remembers files it has already formatted, unless the `--diff` flag is used or -code is passed via standard input. This information is stored per-user. The exact -location of the file depends on the _Black_ version and the system on which _Black_ is -run. The file is non-portable. The standard location on common operating systems is: - -- Windows: - `C:\\Users\\AppData\Local\black\black\Cache\\cache...pickle` -- macOS: - `/Users//Library/Caches/black//cache...pickle` -- Linux: - `/home//.cache/black//cache...pickle` - -`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. - -To override the location of these files on macOS or Linux, set the environment variable -`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 `XDG_CACHE_HOME=.cache`. _Black_ will -then write the above files to `.cache/black//`. diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index f03d247d949..00000000000 --- a/docs/index.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. 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 `_. - -.. note:: - - `Black is beta `_. - - -Testimonials ------------- - -**Dusty Phillips**, `writer `_: - - *Black is opinionated so you don't have to be.* - -**Hynek Schlawack**, creator of `attrs `_, core -developer of Twisted and CPython: - - *An auto-formatter that doesn't suck is all I want for Xmas!* - -**Carl Meyer**, `Django `_ core developer: - - *At least the name is good.* - -**Kenneth Reitz**, creator of `requests `_ -and `pipenv `_: - - *This vastly improves the formatting of our code. Thanks a ton!* - -Contents --------- - -.. toctree:: - :maxdepth: 2 - - installation_and_usage - the_black_code_style - pyproject_toml - compatible_configs - editor_integration - blackd - black_primer - version_control_integration - github_actions - ignoring_unmodified_files - contributing_to_black - show_your_style - change_log - reference/reference_summary - authors - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` diff --git a/docs/installation_and_usage.md b/docs/installation_and_usage.md deleted file mode 100644 index cc0269198a2..00000000000 --- a/docs/installation_and_usage.md +++ /dev/null @@ -1,179 +0,0 @@ -[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md" - -# Installation and usage - -## Installation - -_Black_ can be installed by running `pip install black`. It requires Python 3.6.0+ to -run but you can reformat Python 2 code with it, too. - -### Install from GitHub - -If you can't wait for the latest _hotness_ and want to install from GitHub, use: - -`pip install git+git://github.com/psf/black` - -## Usage - -To get started right away with sensible defaults: - -```sh -black {source_file_or_directory} -``` - -You can run _Black_ as a package if running it as a script doesn't work: - -```sh -python -m black {source_file_or_directory} -``` - -## Command line options - -_Black_ doesn't provide many options. You can list them by running `black --help`: - -```text -Usage: black [OPTIONS] [SRC]... - - The uncompromising code formatter. - -Options: - -c, --code TEXT Format the code passed in as a string. - -l, --line-length INTEGER How many characters per line to allow. - [default: 88] - - -t, --target-version [py27|py33|py34|py35|py36|py37|py38] - Python versions that should be supported by - Black's output. [default: per-file auto- - detection] - - --pyi Format all input files like typing stubs - regardless of file extension (useful when - piping source on standard input). - - -S, --skip-string-normalization - Don't normalize string quotes or prefixes. - --check Don't write the files back, just return the - status. Return code 0 means nothing would - change. Return code 1 means some files - would be reformatted. Return code 123 means - there was an internal error. - - --diff Don't write the files back, just output a - diff for each file on stdout. - - --color / --no-color Show colored diff. Only applies when - `--diff` is given. - - --fast / --safe If --fast given, skip temporary sanity - checks. [default: --safe] - - --include TEXT 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). Exclusions - are calculated first, inclusions later. - [default: \.pyi?$] - - --exclude TEXT 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). - Exclusions are calculated first, inclusions - later. [default: /(\.eggs|\.git|\.hg|\.mypy - _cache|\.nox|\.tox|\.venv|\.svn|_build|buck- - out|build|dist)/] - - --force-exclude TEXT Like --exclude, but files and directories - matching this regex will be excluded even - when they are passed explicitly as arguments - - -q, --quiet Don't emit non-error messages to stderr. - Errors are still emitted; silence those with - 2>/dev/null. - - -v, --verbose Also emit messages to stderr about files - that were not changed or were ignored due to - --exclude=. - - --version Show the version and exit. - --config FILE Read configuration from FILE path. - -h, --help Show this message and exit. -``` - -_Black_ is a well-behaved Unix-style command-line tool: - -- 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 - filename; -- it only outputs messages to users on standard error; -- exits with code 0 unless an internal error occurred (or `--check` was used). - -## Using _Black_ with other tools - -While _Black_ enforces formatting that conforms to PEP 8, other tools may raise warnings -about _Black_'s changes or will overwrite _Black_'s changes. A good example of this is -[isort](https://pypi.org/p/isort). Since _Black_ is barely configurable, these tools -should be configured to neither warn about nor overwrite _Black_'s changes. - -Actual details on _Black_ compatible configurations for various tools can be found in -[compatible_configs](https://github.com/psf/black/blob/master/docs/compatible_configs.md). - -## Migrating your code style without ruining git blame - -A long-standing argument against moving to automated code formatters like _Black_ is -that the migration will clutter up the output of `git blame`. This was a valid argument, -but since Git version 2.23, Git natively supports -[ignoring revisions in blame](https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revltrevgt) -with the `--ignore-rev` option. You can also pass a file listing the revisions to ignore -using the `--ignore-revs-file` option. The changes made by the revision will be ignored -when assigning blame. Lines modified by an ignored revision will be blamed on the -previous revision that modified those lines. - -So when migrating your project's code style to _Black_, reformat everything and commit -the changes (preferably in one massive commit). Then put the full 40 characters commit -identifier(s) into a file. - -``` -# Migrate code style to Black -5b4ab991dede475d393e9d69ec388fd6bd949699 -``` - -Afterwards, you can pass that file to `git blame` and see clean and meaningful blame -information. - -```console -$ git blame important.py --ignore-revs-file .git-blame-ignore-revs -7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 1) def very_important_function(text, file): -abdfd8b0 (Alice Doe 2019-09-23 11:39:32 -0400 2) text = text.lstrip() -7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 3) with open(file, "r+") as f: -7a1ae265 (John Smith 2019-04-15 15:55:13 -0400 4) f.write(formatted) -``` - -You can even configure `git` to automatically ignore revisions listed in a file on every -call to `git blame`. - -```console -$ git config blame.ignoreRevsFile .git-blame-ignore-revs -``` - -**The one caveat is that GitHub and GitLab do not yet support ignoring revisions using -their native UI of blame.** So blame information will be cluttered with a reformatting -commit on those platforms. (If you'd like this feature, there's an open issue for -[GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/31423) and please let GitHub -know!) - -## NOTE: This is a beta product - -_Black_ is already [successfully used](#used-by) by many projects, small and big. It -also sports a decent test suite. However, it is still very new. 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 temporary safety measure, _Black_ will check that the reformatted code still -produces a valid AST that is equivalent to the original. This slows it down. If you're -feeling confident, use `--fast`. diff --git a/docs/integrations/github_actions.md b/docs/integrations/github_actions.md index 12bcb21fee6..ebfcc2d95a2 100644 --- a/docs/integrations/github_actions.md +++ b/docs/integrations/github_actions.md @@ -44,7 +44,9 @@ extra. Installing the extra and including Jupyter Notebook files can be configur `jupyter` (default is `false`). You can also configure the arguments passed to _Black_ via `options` (defaults to -`'--check --diff'`) and `src` (default is `'.'`) +`'--check --diff'`) and `src` (default is `'.'`). Please note that the +[`--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: diff --git a/docs/integrations/source_version_control.md b/docs/integrations/source_version_control.md index 31d0df27273..4189b5c4b06 100644 --- a/docs/integrations/source_version_control.md +++ b/docs/integrations/source_version_control.md @@ -7,7 +7,7 @@ Use [pre-commit](https://pre-commit.com/). Once you ```yaml repos: - repo: https://github.com/psf/black - rev: 22.8.0 + rev: 22.10.0 hooks: - id: black # It is recommended to specify the latest version of Python diff --git a/docs/pyproject_toml.md b/docs/pyproject_toml.md deleted file mode 100644 index cd313452b1e..00000000000 --- a/docs/pyproject_toml.md +++ /dev/null @@ -1,88 +0,0 @@ -[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md" - -# pyproject.toml - -_Black_ is able to read project-specific default values for its command line options -from a `pyproject.toml` file. This is especially useful for specifying custom -`--include` and `--exclude` patterns for your project. - -**Pro-tip**: If you're asking yourself "Do I need to configure anything?" the answer is -"No". _Black_ is all about sensible defaults. - -## What on Earth is a `pyproject.toml` file? - -[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 -of tools like [Poetry](https://python-poetry.org/) or -[Flit](https://flit.readthedocs.io/en/latest/) it can fully replace the need for -`setup.py` and `setup.cfg` files. - -## Where _Black_ looks for the file - -By default _Black_ looks for `pyproject.toml` starting from the common base directory of -all files and directories passed on the command line. If it's not there, it looks in -parent directories. It stops looking when it finds the file, or a `.git` directory, or a -`.hg` directory, or the root of the file system, whichever comes first. - -If you're formatting standard input, _Black_ will look for configuration starting from -the current working directory. - -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. - -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. - -## Configuration format - -As the file extension suggests, `pyproject.toml` is a -[TOML](https://github.com/toml-lang/toml) file. It contains separate sections for -different tools. _Black_ is using the `[tool.black]` section. The option keys are the -same as long names of options on the command line. - -Note that you have to use single-quoted strings in TOML for regular expressions. It's -the equivalent of r-strings in Python. Multiline strings are treated as verbose regular -expressions by Black. Use `[ ]` to denote a significant space character. - -
-Example pyproject.toml - -```toml -[tool.black] -line-length = 88 -target-version = ['py37'] -include = '\.pyi?$' -exclude = ''' - -( - /( - \.eggs # exclude a few common directories in the - | \.git # root of the project - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - )/ - | foo.py # also separately exclude a file named foo.py in - # the root of the project -) -''' -``` - -
- -## Lookup hierarchy - -Command-line options have defaults that you can see in `--help`. A `pyproject.toml` can -override those defaults. Finally, options provided by the user on the command line -override both. - -_Black_ will only ever use one `pyproject.toml` file during an entire run. It doesn't -look for multiple files, and doesn't compose configuration from different levels of the -file hierarchy. diff --git a/docs/reference/reference_functions.rst b/docs/reference/reference_functions.rst deleted file mode 100644 index a7184115c94..00000000000 --- a/docs/reference/reference_functions.rst +++ /dev/null @@ -1,180 +0,0 @@ -*Black* functions -================= - -*Contents are subject to change.* - -.. currentmodule:: black - -Assertions and checks ---------------------- - -.. autofunction:: black.assert_equivalent - -.. autofunction:: black.assert_stable - -.. autofunction:: black.can_be_split - -.. autofunction:: black.can_omit_invisible_parens - -.. autofunction:: black.is_empty_tuple - -.. autofunction:: black.is_import - -.. autofunction:: black.is_line_short_enough - -.. autofunction:: black.is_multiline_string - -.. autofunction:: black.is_one_tuple - -.. autofunction:: black.is_split_after_delimiter - -.. autofunction:: black.is_split_before_delimiter - -.. autofunction:: black.is_stub_body - -.. autofunction:: black.is_stub_suite - -.. autofunction:: black.is_vararg - -.. autofunction:: black.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.lib2to3_parse - -.. autofunction:: black.lib2to3_unparse - -Split functions ---------------- - -.. autofunction:: black.bracket_split_build_line - -.. autofunction:: black.bracket_split_succeeded_or_raise - -.. autofunction:: black.delimiter_split - -.. autofunction:: black.left_hand_split - -.. autofunction:: black.right_hand_split - -.. autofunction:: black.standalone_comment_split - -.. autofunction:: black.transform_line - -Caching -------- - -.. autofunction:: black.filter_cached - -.. autofunction:: black.get_cache_file - -.. autofunction:: black.get_cache_info - -.. autofunction:: black.read_cache - -.. autofunction:: black.write_cache - -Utilities ---------- - -.. py:function:: black.DebugVisitor.show(code: str) -> None - - Pretty-print the lib2to3 AST of a given string of `code`. - -.. autofunction:: black.cancel - -.. autofunction:: black.child_towards - -.. autofunction:: black.container_of - -.. autofunction:: black.convert_one_fmt_off_pair - -.. autofunction:: black.diff - -.. autofunction:: black.dont_increase_indentation - -.. autofunction:: black.format_float_or_int_string - -.. autofunction:: black.ensure_visible - -.. autofunction:: black.enumerate_reversed - -.. autofunction:: black.enumerate_with_length - -.. autofunction:: black.generate_comments - -.. autofunction:: black.generate_ignored_nodes - -.. autofunction:: black.is_fmt_on - -.. autofunction:: black.contains_fmt_on_at_column - -.. autofunction:: black.first_leaf_column - -.. autofunction:: black.generate_trailers_to_omit - -.. autofunction:: black.get_future_imports - -.. autofunction:: black.list_comments - -.. autofunction:: black.make_comment - -.. autofunction:: black.maybe_make_parens_invisible_in_atom - -.. autofunction:: black.max_delimiter_priority_in_atom - -.. autofunction:: black.normalize_fmt_off - -.. autofunction:: black.normalize_numeric_literal - -.. autofunction:: black.normalize_prefix - -.. autofunction:: black.normalize_string_prefix - -.. autofunction:: black.normalize_string_quotes - -.. autofunction:: black.normalize_invisible_parens - -.. autofunction:: black.patch_click - -.. autofunction:: black.preceding_leaf - -.. autofunction:: black.re_compile_maybe_verbose - -.. autofunction:: black.should_split_body_explode - -.. autofunction:: black.shutdown - -.. autofunction:: black.sub_twice - -.. autofunction:: black.whitespace diff --git a/docs/requirements.txt b/docs/requirements.txt index 3c4b43511f6..c2342a35d7b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ # Used by ReadTheDocs; pinned requirements for stability. myst-parser==0.18.1 -Sphinx==5.2.3 +Sphinx==5.3.0 # Older versions break Sphinx even though they're declared to be supported. docutils==0.19 sphinxcontrib-programoutput==0.17 diff --git a/docs/show_your_style.md b/docs/show_your_style.md deleted file mode 100644 index 67b213c3965..00000000000 --- a/docs/show_your_style.md +++ /dev/null @@ -1,19 +0,0 @@ -[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md" - -# 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: - -``` -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black -``` - -Looks like this: -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index a028a2888ed..17b7eef092f 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -63,26 +63,47 @@ limit. Line continuation backslashes are converted into parenthesized strings. Unnecessary parentheses are stripped. The stability and status of this feature is tracked in [this issue](https://github.com/psf/black/issues/2188). -### Removing newlines in the beginning of code blocks +### Improved empty line management -_Black_ will remove newlines in the beginning of new code blocks, i.e. when the -indentation level is increased. For example: +1. _Black_ will remove newlines in the beginning of new code blocks, i.e. when the + indentation level is increased. For example: -```python -def my_func(): + ```python + def my_func(): - print("The line above me will be deleted!") -``` + print("The line above me will be deleted!") + ``` -will be changed to: + will be changed to: + + ```python + def my_func(): + print("The line above me will be deleted!") + ``` + + This new feature will be applied to **all code blocks**: `def`, `class`, `if`, + `for`, `while`, `with`, `case` and `match`. + +2. _Black_ will enforce empty lines before classes and functions with leading comments. + For example: + + ```python + some_var = 1 + # Leading sticky comment + def my_func(): + ... + ``` + + will be changed to: + + ```python + some_var = 1 -```python -def my_func(): - print("The line above me will be deleted!") -``` -This new feature will be applied to **all code blocks**: `def`, `class`, `if`, `for`, -`while`, `with`, `case` and `match`. + # Leading sticky comment + def my_func(): + ... + ``` ### Improved parentheses management diff --git a/docs/usage_and_configuration/black_as_a_server.md b/docs/usage_and_configuration/black_as_a_server.md index a2d4252109a..f24fb34d915 100644 --- a/docs/usage_and_configuration/black_as_a_server.md +++ b/docs/usage_and_configuration/black_as_a_server.md @@ -50,6 +50,9 @@ is rejected with `HTTP 501` (Not Implemented). The headers controlling how source code is formatted are: - `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` command line flag. If present and its value is not the empty string, no string normalization will be performed. diff --git a/docs/usage_and_configuration/the_basics.md b/docs/usage_and_configuration/the_basics.md index aa176c4ba3f..80897532a68 100644 --- a/docs/usage_and_configuration/the_basics.md +++ b/docs/usage_and_configuration/the_basics.md @@ -76,6 +76,8 @@ _Black_ to just tell you what it _would_ do without actually rewriting the Pytho There's two variations to this mode that are independently enabled by their respective flags. Both variations can be enabled at once. +(labels/exit-code)= + #### Exit code Passing `--check` will make _Black_ exit with: @@ -173,7 +175,7 @@ You can check the version of _Black_ you have installed using the `--version` fl ```console $ black --version -black, version 22.8.0 +black, version 22.10.0 ``` An option to require a specific version to be running is also provided. diff --git a/docs/version_control_integration.md b/docs/version_control_integration.md deleted file mode 100644 index 25dac308c47..00000000000 --- a/docs/version_control_integration.md +++ /dev/null @@ -1,28 +0,0 @@ -[//]: # "NOTE: THIS FILE WAS AUTOGENERATED FROM README.md" - -# Version control integration - -Use [pre-commit](https://pre-commit.com/). Once you -[have it installed](https://pre-commit.com/#install), add this to the -`.pre-commit-config.yaml` in your repository: - -```yaml -repos: - - repo: https://github.com/psf/black - rev: 19.10b0 # Replace by any tag/version: https://github.com/psf/black/tags - hooks: - - id: black - language_version: python3 # Should be a command that runs python3.6+ -``` - -Then run `pre-commit install` and you're ready to go. - -Avoid using `args` in the hook. Instead, store necessary configuration in -`pyproject.toml` so that editors and command-line usage of Black all behave consistently -for your project. See _Black_'s own -[pyproject.toml](https://github.com/psf/black/blob/master/pyproject.toml) for an -example. - -If you're already using Python 3.7, switch the `language_version` accordingly. Finally, -`stable` is a branch that tracks the latest release on PyPI. If you'd rather run on -master, this is also an option. diff --git a/fuzz.py b/fuzz.py deleted file mode 100644 index fdd4917f2ec..00000000000 --- a/fuzz.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Property-based tests for Black. - -By Zac Hatfield-Dodds, based on my Hypothesmith tool for source code -generation. You can run this file with `python`, `pytest`, or (soon) -a coverage-guided fuzzer I'm working on. -""" - -import hypothesmith -from hypothesis import HealthCheck, given, settings, strategies as st - -import black - - -# This test uses the Hypothesis and Hypothesmith libraries to generate random -# syntatically-valid Python source code and run Black in odd modes. -@settings( - max_examples=1000, # roughly 1k tests/minute, or half that under coverage - derandomize=True, # deterministic mode to avoid CI flakiness - deadline=None, # ignore Hypothesis' health checks; we already know that - suppress_health_check=HealthCheck.all(), # this is slow and filter-heavy. -) -@given( - # Note that while Hypothesmith might generate code unlike that written by - # humans, it's a general test that should pass for any *valid* source code. - # (so e.g. running it against code scraped of the internet might also help) - src_contents=hypothesmith.from_grammar() | hypothesmith.from_node(), - # Using randomly-varied modes helps us to exercise less common code paths. - mode=st.builds( - black.FileMode, - line_length=st.just(88) | st.integers(0, 200), - string_normalization=st.booleans(), - is_pyi=st.booleans(), - ), -) -def test_idempotent_any_syntatically_valid_python( - src_contents: str, mode: black.FileMode -) -> None: - # Before starting, let's confirm that the input string is valid Python: - compile(src_contents, "", "exec") # else the bug is in hypothesmith - - # Then format the code... - try: - 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 code with it. See issues #970, #1012, #1358, and #1557. - # TODO: remove this try-except block when issues are resolved. - return - - # And check that we got equivalent and stable output. - black.assert_equivalent(src_contents, dst_contents) - black.assert_stable(src_contents, dst_contents, mode=mode) - - # Future test: check that pure-python and mypyc versions of black - # give identical output for identical input? - - -if __name__ == "__main__": - test_idempotent_any_syntatically_valid_python() diff --git a/mypy.ini b/mypy.ini index 4811cc0be76..58bb7536173 100644 --- a/mypy.ini +++ b/mypy.ini @@ -2,7 +2,7 @@ # 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 +python_version=3.7 mypy_path=src @@ -19,11 +19,6 @@ no_implicit_reexport = False # to avoid 'em in the first place. warn_unreachable=True -[mypy-black] -# The following is because of `patch_click()`. Remove when -# we drop Python 3.6 support. -warn_unused_ignores=False - [mypy-blib2to3.driver.*] ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index 554d7d07bf3..329adaa65d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ build-backend = "hatchling.build" [project] name = "black" description = "The uncompromising code formatter." -license = "MIT" +license = { text = "MIT" } requires-python = ">=3.7" authors = [ { name = "Łukasz Langa", email = "lukasz@langa.pl" }, diff --git a/setup.py b/setup.py deleted file mode 100644 index 12fde2568cf..00000000000 --- a/setup.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (C) 2020 Łukasz Langa -from setuptools import setup -import sys -import os - -assert sys.version_info >= (3, 6, 0), "black requires Python 3.6+" -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/master/CHANGES.md"}, - license="MIT", - py_modules=["_black_version"], - ext_modules=ext_modules, - packages=["blackd", "black", "blib2to3", "blib2to3.pgen2", "black_primer"], - package_dir={"": "src"}, - package_data={"blib2to3": ["*.txt"], "black": ["py.typed"]}, - python_requires=">=3.6", - zip_safe=False, - install_requires=[ - "click>=7.1.2", - "appdirs", - "toml>=0.10.1", - "typed-ast>=1.4.0", - "regex>=2020.1.8", - "pathspec>=0.6, <1", - "dataclasses>=0.6; python_version < '3.7'", - "typing_extensions>=3.7.4", - "mypy_extensions>=0.4.3", - ], - extras_require={ - "d": ["aiohttp>=3.3.2", "aiohttp-cors"], - "colorama": ["colorama>=0.4.3"], - }, - 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 :: 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", - ] - }, -) diff --git a/src/black/__init__.py b/src/black/__init__.py index 5b8c9749119..94592278c31 100644 --- a/src/black/__init__.py +++ b/src/black/__init__.py @@ -61,7 +61,7 @@ unmask_cell, ) from black.linegen import LN, LineGenerator, transform_line -from black.lines import EmptyLineTracker, Line +from black.lines import EmptyLineTracker, LinesBlock from black.mode import ( FUTURE_FLAG_TO_FEATURE, VERSION_TO_FEATURES, @@ -248,6 +248,12 @@ def validate_regex( ), default=[], ) +@click.option( + "-x", + "--skip-source-first-line", + is_flag=True, + help="Skip the first line of the source code.", +) @click.option( "-S", "--skip-string-normalization", @@ -428,6 +434,7 @@ def main( # noqa: C901 pyi: bool, ipynb: bool, python_cell_magics: Sequence[str], + skip_source_first_line: bool, skip_string_normalization: bool, skip_magic_trailing_comma: bool, experimental_string_processing: bool, @@ -490,8 +497,10 @@ def main( # noqa: C901 user_level_config = str(find_user_pyproject_toml()) if config == user_level_config: out( - "Using configuration from user-level config at " - f"'{user_level_config}'.", + ( + "Using configuration from user-level config at " + f"'{user_level_config}'." + ), fg="blue", ) elif config_source in ( @@ -528,6 +537,7 @@ def main( # noqa: C901 line_length=line_length, is_pyi=pyi, is_ipynb=ipynb, + skip_source_first_line=skip_source_first_line, string_normalization=not skip_string_normalization, magic_trailing_comma=not skip_magic_trailing_comma, experimental_string_processing=experimental_string_processing, @@ -790,7 +800,10 @@ def format_file_in_place( mode = replace(mode, is_ipynb=True) then = datetime.utcfromtimestamp(src.stat().st_mtime) + header = b"" with open(src, "rb") as buf: + if mode.skip_source_first_line: + header = buf.readline() src_contents, encoding, newline = decode_bytes(buf.read()) try: dst_contents = format_file_contents(src_contents, fast=fast, mode=mode) @@ -800,6 +813,8 @@ def format_file_in_place( raise ValueError( f"File '{src}' cannot be parsed as valid Jupyter notebook." ) from None + src_contents = header.decode(encoding) + src_contents + dst_contents = header.decode(encoding) + dst_contents if write_back == WriteBack.YES: with open(src, "w", encoding=encoding, newline=newline) as f: @@ -1062,7 +1077,7 @@ def f( def _format_str_once(src_contents: str, *, mode: Mode) -> str: src_node = lib2to3_parse(src_contents.lstrip(), mode.target_versions) - dst_contents = [] + dst_blocks: List[LinesBlock] = [] if mode.target_versions: versions = mode.target_versions else: @@ -1071,22 +1086,25 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str: normalize_fmt_off(src_node, preview=mode.preview) lines = LineGenerator(mode=mode) - elt = EmptyLineTracker(is_pyi=mode.is_pyi) - empty_line = Line(mode=mode) - after = 0 + elt = EmptyLineTracker(mode=mode) split_line_features = { feature for feature in {Feature.TRAILING_COMMA_IN_CALL, Feature.TRAILING_COMMA_IN_DEF} if supports_feature(versions, feature) } + block: Optional[LinesBlock] = None for current_line in lines.visit(src_node): - dst_contents.append(str(empty_line) * after) - before, after = elt.maybe_empty_lines(current_line) - dst_contents.append(str(empty_line) * before) + block = elt.maybe_empty_lines(current_line) + dst_blocks.append(block) for line in transform_line( current_line, mode=mode, features=split_line_features ): - dst_contents.append(str(line)) + block.content_lines.append(str(line)) + if dst_blocks: + dst_blocks[-1].after = 0 + dst_contents = [] + for block in dst_blocks: + dst_contents.extend(block.all_lines()) return "".join(dst_contents) @@ -1369,9 +1387,9 @@ def patch_click() -> None: for module in modules: if hasattr(module, "_verify_python3_env"): - module._verify_python3_env = lambda: None # type: ignore + module._verify_python3_env = lambda: None if hasattr(module, "_verify_python_env"): - module._verify_python_env = lambda: None # type: ignore + module._verify_python_env = lambda: None def patched_main() -> None: diff --git a/src/black/concurrency.py b/src/black/concurrency.py index 10e288f4f93..1598f51e43f 100644 --- a/src/black/concurrency.py +++ b/src/black/concurrency.py @@ -47,12 +47,8 @@ def cancel(tasks: Iterable["asyncio.Task[Any]"]) -> None: def shutdown(loop: asyncio.AbstractEventLoop) -> None: """Cancel all pending tasks on `loop`, wait for them, and close the loop.""" try: - if sys.version_info[:2] >= (3, 7): - all_tasks = asyncio.all_tasks - else: - all_tasks = asyncio.Task.all_tasks # This part is borrowed from asyncio/runners.py in Python 3.7b2. - to_cancel = [task for task in all_tasks(loop) if not task.done()] + to_cancel = [task for task in asyncio.all_tasks(loop) if not task.done()] if not to_cancel: return diff --git a/src/black/lines.py b/src/black/lines.py index dd11dde7b55..9cda559c4ee 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -1,29 +1,38 @@ import itertools import sys from dataclasses import dataclass, field -from typing import Callable, Dict, Iterator, List, Optional, Sequence, Tuple, TypeVar, cast +from typing import ( + Callable, + Dict, + Iterator, + List, + Optional, + Sequence, + Tuple, + TypeVar, + cast, +) from black.brackets import DOT_PRIORITY, BracketTracker from black.mode import Mode, Preview from black.nodes import ( - BRACKETS, - CLOSING_BRACKETS, - OPENING_BRACKETS, - STANDALONE_COMMENT, - TEST_DESCENDANTS, - child_towards, - is_import, - is_multiline_string, - is_one_sequence_between, - is_type_comment, - replace_child, - syms, - whitespace, + BRACKETS, + CLOSING_BRACKETS, + OPENING_BRACKETS, + STANDALONE_COMMENT, + TEST_DESCENDANTS, + child_towards, + is_import, + is_multiline_string, + is_one_sequence_between, + is_type_comment, + replace_child, + syms, + whitespace, ) from blib2to3.pgen2 import token from blib2to3.pytree import Leaf, Node - # types T = TypeVar("T") Index = int @@ -439,6 +448,28 @@ def __bool__(self) -> bool: return bool(self.leaves or self.comments) +@dataclass +class LinesBlock: + """Class that holds information about a block of formatted lines. + + This is introduced so that the EmptyLineTracker can look behind the standalone + comments and adjust their empty lines for class or def lines. + """ + + mode: Mode + previous_block: Optional["LinesBlock"] + original_line: Line + before: int = 0 + content_lines: List[str] = field(default_factory=list) + after: int = 0 + + def all_lines(self) -> List[str]: + empty_line = str(Line(mode=self.mode)) + return ( + [empty_line * self.before] + self.content_lines + [empty_line * self.after] + ) + + @dataclass class EmptyLineTracker: """Provides a stateful method that returns the number of potential extra @@ -449,33 +480,55 @@ class EmptyLineTracker: are consumed by `maybe_empty_lines()` and included in the computation. """ - is_pyi: bool = False + mode: Mode previous_line: Optional[Line] = None - previous_after: int = 0 + previous_block: Optional[LinesBlock] = None previous_defs: List[int] = field(default_factory=list) + semantic_leading_comment: Optional[LinesBlock] = None - def maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: + def maybe_empty_lines(self, current_line: Line) -> LinesBlock: """Return the number of extra empty lines before and after the `current_line`. This is for separating `def`, `async def` and `class` with extra empty lines (two on module-level). """ before, after = self._maybe_empty_lines(current_line) + previous_after = self.previous_block.after if self.previous_block else 0 before = ( # Black should not insert empty lines at the beginning # of the file 0 if self.previous_line is None - else before - self.previous_after + else before - previous_after ) - self.previous_after = after + block = LinesBlock( + mode=self.mode, + previous_block=self.previous_block, + original_line=current_line, + before=before, + after=after, + ) + + # Maintain the semantic_leading_comment state. + if current_line.is_comment: + if self.previous_line is None or ( + not self.previous_line.is_decorator + # `or before` means this comment already has an empty line before + and (not self.previous_line.is_comment or before) + and (self.semantic_leading_comment is None or before) + ): + self.semantic_leading_comment = block + elif not current_line.is_decorator: + self.semantic_leading_comment = None + self.previous_line = current_line - return before, after + self.previous_block = block + return block def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: max_allowed = 1 if current_line.depth == 0: - max_allowed = 1 if self.is_pyi else 2 + max_allowed = 1 if self.mode.is_pyi else 2 if current_line.leaves: # Consume the first leaf's extra newlines. first_leaf = current_line.leaves[0] @@ -486,7 +539,7 @@ def _maybe_empty_lines(self, current_line: Line) -> Tuple[int, int]: before = 0 depth = current_line.depth while self.previous_defs and self.previous_defs[-1] >= depth: - if self.is_pyi: + if self.mode.is_pyi: assert self.previous_line is not None if depth and not current_line.is_def and self.previous_line.is_def: # Empty lines between attributes and methods should be preserved. @@ -554,7 +607,7 @@ def _maybe_empty_lines_for_class_or_def( return 0, 0 if self.previous_line.is_decorator: - if self.is_pyi and current_line.is_stub_class: + if self.mode.is_pyi and current_line.is_stub_class: # Insert an empty line after a decorated stub class return 0, 1 @@ -565,14 +618,27 @@ def _maybe_empty_lines_for_class_or_def( ): return 0, 0 + comment_to_add_newlines: Optional[LinesBlock] = None if ( self.previous_line.is_comment and self.previous_line.depth == current_line.depth and before == 0 ): - return 0, 0 + slc = self.semantic_leading_comment + if ( + Preview.empty_lines_before_class_or_def_with_leading_comments + in current_line.mode + and slc is not None + and slc.previous_block is not None + and not slc.previous_block.original_line.is_class + and not slc.previous_block.original_line.opens_block + and slc.before <= 1 + ): + comment_to_add_newlines = slc + else: + return 0, 0 - if self.is_pyi: + if self.mode.is_pyi: if current_line.is_class or self.previous_line.is_class: if self.previous_line.depth < current_line.depth: newlines = 0 @@ -600,6 +666,13 @@ def _maybe_empty_lines_for_class_or_def( newlines = 0 else: newlines = 1 if current_line.depth else 2 + if comment_to_add_newlines is not None: + previous_block = comment_to_add_newlines.previous_block + if previous_block is not None: + comment_to_add_newlines.before = ( + max(comment_to_add_newlines.before, newlines) - previous_block.after + ) + newlines = 0 return newlines, 0 diff --git a/src/black/mode.py b/src/black/mode.py index 6c0847e8bcc..e2eff2391b1 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -150,6 +150,7 @@ class Preview(Enum): """Individual preview style features.""" annotation_parens = auto() + empty_lines_before_class_or_def_with_leading_comments = auto() long_docstring_quotes_on_newline = auto() normalize_docstring_quotes_and_prefixes_properly = auto() one_element_subscript = auto() @@ -170,6 +171,7 @@ class Mode: string_normalization: bool = True is_pyi: bool = False is_ipynb: bool = False + skip_source_first_line: bool = False magic_trailing_comma: bool = True experimental_string_processing: bool = False python_cell_magics: Set[str] = field(default_factory=set) @@ -178,8 +180,10 @@ class Mode: def __post_init__(self) -> None: if self.experimental_string_processing: warn( - "`experimental string processing` has been included in `preview`" - " and deprecated. Use `preview` instead.", + ( + "`experimental string processing` has been included in `preview`" + " and deprecated. Use `preview` instead." + ), Deprecated, ) @@ -208,6 +212,7 @@ def get_cache_key(self) -> str: str(int(self.string_normalization)), str(int(self.is_pyi)), str(int(self.is_ipynb)), + str(int(self.skip_source_first_line)), str(int(self.magic_trailing_comma)), str(int(self.experimental_string_processing)), str(int(self.preview)), diff --git a/src/black/parsing.py b/src/black/parsing.py index 64c0b1e3018..c37c12b868d 100644 --- a/src/black/parsing.py +++ b/src/black/parsing.py @@ -27,12 +27,13 @@ try: from typed_ast import ast3 except ImportError: - # Either our python version is too low, or we're on pypy - if sys.version_info < (3, 7) or (sys.version_info < (3, 8) and not _IS_PYPY): + if sys.version_info < (3, 8) and not _IS_PYPY: print( - "The typed_ast package is required but not installed.\n" - "You can upgrade to Python 3.8+ or install typed_ast with\n" - "`python3 -m pip install typed-ast`.", + ( + "The typed_ast package is required but not installed.\n" + "You can upgrade to Python 3.8+ or install typed_ast with\n" + "`python3 -m pip install typed-ast`." + ), file=sys.stderr, ) sys.exit(1) @@ -166,8 +167,6 @@ def parse_single_version( # comments separately. return ast3.parse(src, filename, feature_version=version[1]) - raise AssertionError("INTERNAL ERROR: Tried parsing unsupported Python version!") - def parse_ast(src: str) -> Union[ast.AST, ast3.AST]: # TODO: support Python 4+ ;) diff --git a/src/black/trans.py b/src/black/trans.py index 7e2d8e67c1a..8893ab02aab 100644 --- a/src/black/trans.py +++ b/src/black/trans.py @@ -1058,33 +1058,19 @@ def _prefer_paren_wrap_match(LL: List[Leaf]) -> Optional[int]: if LL[0].type != token.STRING: return None - matching_nodes = [ - syms.listmaker, - syms.dictsetmaker, - syms.testlist_gexp, - ] - # If the string is an immediate child of a list/set/tuple literal... - if ( - parent_type(LL[0]) in matching_nodes - or parent_type(LL[0].parent) in matching_nodes + # If the string is surrounded by commas (or is the first/last child)... + prev_sibling = LL[0].prev_sibling + next_sibling = LL[0].next_sibling + if not prev_sibling and not next_sibling and parent_type(LL[0]) == syms.atom: + # If it's an atom string, we need to check the parent atom's siblings. + parent = LL[0].parent + assert parent is not None # For type checkers. + prev_sibling = parent.prev_sibling + next_sibling = parent.next_sibling + if (not prev_sibling or prev_sibling.type == token.COMMA) and ( + not next_sibling or next_sibling.type == token.COMMA ): - # And the string is surrounded by commas (or is the first/last child)... - prev_sibling = LL[0].prev_sibling - next_sibling = LL[0].next_sibling - if ( - not prev_sibling - and not next_sibling - and parent_type(LL[0]) == syms.atom - ): - # If it's an atom string, we need to check the parent atom's siblings. - parent = LL[0].parent - assert parent is not None # For type checkers. - prev_sibling = parent.prev_sibling - next_sibling = parent.next_sibling - if (not prev_sibling or prev_sibling.type == token.COMMA) and ( - not next_sibling or next_sibling.type == token.COMMA - ): - return 0 + return 0 return None @@ -1653,9 +1639,8 @@ class StringParenWrapper(BaseStringSplitter, CustomSplitMapMixin): assigned the value of some string. OR * The line starts with an "atom" string that prefers to be wrapped in - parens. It's preferred to be wrapped when it's is an immediate child of - a list/set/tuple literal, AND the string is surrounded by commas (or is - the first/last child). + parens. It's preferred to be wrapped when the string is surrounded by + commas (or is the first/last child). Transformations: The chosen string is wrapped in parentheses and then split at the LPAR. diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json deleted file mode 100644 index 546f47782cd..00000000000 --- a/src/black_primer/primer.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "configuration_format_version": 20200509, - "projects": { - "aioexabgp": { - "cli_arguments": [], - "expect_formatting_changes": false, - "git_clone_url": "https://github.com/cooperlees/aioexabgp.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "attrs": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/python-attrs/attrs.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "bandersnatch": { - "cli_arguments": [], - "expect_formatting_changes": false, - "git_clone_url": "https://github.com/pypa/bandersnatch.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "channels": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/django/channels.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "django": { - "disabled_reason": "black --check --diff returned 123 on two files", - "disabled": true, - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/django/django.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "flake8-bugbear": { - "cli_arguments": [], - "expect_formatting_changes": false, - "git_clone_url": "https://github.com/PyCQA/flake8-bugbear.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "hypothesis": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/HypothesisWorks/hypothesis.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "pandas": { - "disabled_reason": "black --check --diff returned 123 on one file", - "disabled": true, - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/pandas-dev/pandas.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "poetry": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/python-poetry/poetry.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "pyramid": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/Pylons/pyramid.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "ptr": { - "cli_arguments": [], - "expect_formatting_changes": false, - "git_clone_url": "https://github.com/facebookincubator/ptr.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "pytest": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/pytest-dev/pytest.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "sqlalchemy": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/sqlalchemy/sqlalchemy.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "tox": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/tox-dev/tox.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "virtualenv": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/pypa/virtualenv.git", - "long_checkout": false, - "py_versions": ["all"] - }, - "warehouse": { - "cli_arguments": [], - "expect_formatting_changes": true, - "git_clone_url": "https://github.com/pypa/warehouse.git", - "long_checkout": false, - "py_versions": ["all"] - } - } -} diff --git a/src/blackd/__init__.py b/src/blackd/__init__.py index 6bbc7c52086..ba4750b8298 100644 --- a/src/blackd/__init__.py +++ b/src/blackd/__init__.py @@ -30,6 +30,7 @@ PROTOCOL_VERSION_HEADER = "X-Protocol-Version" LINE_LENGTH_HEADER = "X-Line-Length" PYTHON_VARIANT_HEADER = "X-Python-Variant" +SKIP_SOURCE_FIRST_LINE = "X-Skip-Source-First-Line" SKIP_STRING_NORMALIZATION_HEADER = "X-Skip-String-Normalization" SKIP_MAGIC_TRAILING_COMMA = "X-Skip-Magic-Trailing-Comma" PREVIEW = "X-Preview" @@ -40,6 +41,7 @@ PROTOCOL_VERSION_HEADER, LINE_LENGTH_HEADER, PYTHON_VARIANT_HEADER, + SKIP_SOURCE_FIRST_LINE, SKIP_STRING_NORMALIZATION_HEADER, SKIP_MAGIC_TRAILING_COMMA, PREVIEW, @@ -111,6 +113,9 @@ async def handle(request: web.Request, executor: Executor) -> web.Response: skip_magic_trailing_comma = bool( request.headers.get(SKIP_MAGIC_TRAILING_COMMA, False) ) + skip_source_first_line = bool( + request.headers.get(SKIP_SOURCE_FIRST_LINE, False) + ) preview = bool(request.headers.get(PREVIEW, False)) fast = False if request.headers.get(FAST_OR_SAFE_HEADER, "safe") == "fast": @@ -119,6 +124,7 @@ async def handle(request: web.Request, executor: Executor) -> web.Response: target_versions=versions, is_pyi=pyi, line_length=line_length, + skip_source_first_line=skip_source_first_line, string_normalization=not skip_string_normalization, magic_trailing_comma=not skip_magic_trailing_comma, preview=preview, @@ -128,6 +134,12 @@ async def handle(request: web.Request, executor: Executor) -> web.Response: req_str = req_bytes.decode(charset) then = datetime.utcnow() + header = "" + if skip_source_first_line: + first_newline_position: int = req_str.find("\n") + 1 + header = req_str[:first_newline_position] + req_str = req_str[first_newline_position:] + loop = asyncio.get_event_loop() formatted_str = await loop.run_in_executor( executor, partial(black.format_file_contents, req_str, fast=fast, mode=mode) @@ -140,6 +152,10 @@ async def handle(request: web.Request, executor: Executor) -> web.Response: if formatted_str == req_str: raise black.NothingChanged + # Put the source first line back + req_str = header + req_str + formatted_str = header + formatted_str + # Only output the diff in the HTTP response only_diff = bool(request.headers.get(DIFF_HEADER, False)) if only_diff: diff --git a/src/blib2to3/Grammar.txt b/src/blib2to3/Grammar.txt index ac7ad7643ff..bd8a452a386 100644 --- a/src/blib2to3/Grammar.txt +++ b/src/blib2to3/Grammar.txt @@ -186,7 +186,7 @@ arglist: argument (',' argument)* [','] # multiple (test comp_for) arguments are blocked; keyword unpackings # that precede iterable unpackings are blocked; etc. argument: ( test [comp_for] | - test ':=' test | + test ':=' test [comp_for] | test 'as' test | test '=' asexpr_test | '**' test | diff --git a/test_requirements.txt b/test_requirements.txt index 5bc494d5999..ef61a1210ee 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,6 +1,6 @@ coverage >= 5.3 pre-commit pytest >= 6.1.1 -pytest-xdist >= 2.2.1 +pytest-xdist >= 2.2.1, < 3.0.2 pytest-cov >= 2.11.1 tox diff --git a/tests/data/comments7.py b/tests/data/comments7.py deleted file mode 100644 index 0e2bd35bf55..00000000000 --- a/tests/data/comments7.py +++ /dev/null @@ -1,266 +0,0 @@ -from .config import ( - Any, - Bool, - ConfigType, - ConfigTypeAttributes, - Int, - Path, - # String, - # resolve_to_config_type, - # DEFAULT_TYPE_ATTRIBUTES, -) - - -from .config import ( - Any, - Bool, - ConfigType, - ConfigTypeAttributes, - Int, - no_comma_here_yet - # and some comments, - # resolve_to_config_type, - # DEFAULT_TYPE_ATTRIBUTES, -) -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent # NOT DRY -) -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent as component # DRY -) - - -result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - -result = ( - 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -) - -result = ( - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa -) - -result = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # aaa - - -def func(): - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0789, - a[-1], # type: ignore - ) - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0789, - a[-1] # type: ignore - ) - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - a[-1] # type: ignore - ) - - # The type: ignore exception only applies to line length, not - # other types of formatting. - c = call( - "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", # type: ignore - "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa", "aaaaaaaa" - ) - - -class C: - @pytest.mark.parametrize( - ("post_data", "message"), - [ - # metadata_version errors. - ( - {}, - "None is an invalid value for Metadata-Version. Error: This field is" - " required. see" - " https://packaging.python.org/specifications/core-metadata" - ), - ( - {"metadata_version": "-1"}, - "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" - " Version see" - " https://packaging.python.org/specifications/core-metadata" - ), - # name errors. - ( - {"metadata_version": "1.2"}, - "'' is an invalid value for Name. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata" - ), - ( - {"metadata_version": "1.2", "name": "foo-"}, - "'foo-' is an invalid value for Name. Error: Must start and end with a" - " letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata" - ), - # version errors. - ( - {"metadata_version": "1.2", "name": "example"}, - "'' is an invalid value for Version. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata" - ), - ( - {"metadata_version": "1.2", "name": "example", "version": "dog"}, - "'dog' is an invalid value for Version. Error: Must start and end with" - " a letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata" - ) - ] - ) - def test_fails_invalid_post_data( - self, pyramid_config, db_request, post_data, message - ): - ... - -# output - -from .config import ( - Any, - Bool, - ConfigType, - ConfigTypeAttributes, - Int, - Path, - # String, - # resolve_to_config_type, - # DEFAULT_TYPE_ATTRIBUTES, -) - - -from .config import ( - Any, - Bool, - ConfigType, - ConfigTypeAttributes, - Int, - no_comma_here_yet, - # and some comments, - # resolve_to_config_type, - # DEFAULT_TYPE_ATTRIBUTES, -) -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent, # NOT DRY -) -from com.my_lovely_company.my_lovely_team.my_lovely_project.my_lovely_component import ( - MyLovelyCompanyTeamProjectComponent as component, # DRY -) - - -result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - -result = 1 # look ma, no comment migration xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - -result = ( # aaa - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -) - -result = ( # aaa - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -) - - -def func(): - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0789, - a[-1], # type: ignore - ) - c = call(0.0123, 0.0456, 0.0789, 0.0123, 0.0789, a[-1]) # type: ignore - c = call( - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - 0.0123, - 0.0456, - 0.0789, - a[-1], # type: ignore - ) - - # The type: ignore exception only applies to line length, not - # other types of formatting. - c = call( - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", # type: ignore - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - "aaaaaaaa", - ) - - -class C: - @pytest.mark.parametrize( - ("post_data", "message"), - [ - # metadata_version errors. - ( - {}, - "None is an invalid value for Metadata-Version. Error: This field is" - " required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "-1"}, - "'-1' is an invalid value for Metadata-Version. Error: Unknown Metadata" - " Version see" - " https://packaging.python.org/specifications/core-metadata", - ), - # name errors. - ( - {"metadata_version": "1.2"}, - "'' is an invalid value for Name. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "1.2", "name": "foo-"}, - "'foo-' is an invalid value for Name. Error: Must start and end with a" - " letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - # version errors. - ( - {"metadata_version": "1.2", "name": "example"}, - "'' is an invalid value for Version. Error: This field is required. see" - " https://packaging.python.org/specifications/core-metadata", - ), - ( - {"metadata_version": "1.2", "name": "example", "version": "dog"}, - "'dog' is an invalid value for Version. Error: Must start and end with" - " a letter or numeral and contain only ascii numeric and '.', '_' and" - " '-'. see https://packaging.python.org/specifications/core-metadata", - ), - ], - ) - def test_fails_invalid_post_data( - self, pyramid_config, db_request, post_data, message - ): - ... \ No newline at end of file diff --git a/tests/data/composition_no_trailing_comma.py b/tests/data/composition_no_trailing_comma.py deleted file mode 100644 index f17b89dea8d..00000000000 --- a/tests/data/composition_no_trailing_comma.py +++ /dev/null @@ -1,367 +0,0 @@ -class C: - def test(self) -> None: - with patch("black.out", print): - self.assertEqual( - unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." - ) - self.assertEqual( - unstyle(str(report)), - "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 1 file left unchanged, 1 file failed to" - " reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", - ) - for i in (a,): - if ( - # Rule 1 - i % 2 == 0 - # Rule 2 - and i % 3 == 0 - ): - while ( - # Just a comment - call() - # Another - ): - print(i) - xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( - push_manager=context.request.resource_manager, - max_items_to_push=num_items, - batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE - ).push( - # Only send the first n items. - items=items[:num_items] - ) - return ( - 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) - ) - - def omitting_trailers(self) -> None: - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex] - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] - d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ - 22 - ] - assignment = ( - some.rather.elaborate.rule() and another.rule.ending_with.index[123] - ) - - def easy_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - } == expected, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - } - - def tricky_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - } == expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" - - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - } == expected, ( - "Not what we expected and the message is too long to fit in one line" - ) - - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ) == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - }, ( - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) - - dis_c_instance_method = """\ - %3d 0 LOAD_FAST 1 (x) - 2 LOAD_CONST 1 (1) - 4 COMPARE_OP 2 (==) - 6 LOAD_FAST 0 (self) - 8 STORE_ATTR 0 (x) - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE - """ % ( - _C.__init__.__code__.co_firstlineno + 1, - ) - - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9 - } - ) - - - -# output - -class C: - def test(self) -> None: - with patch("black.out", print): - self.assertEqual( - unstyle(str(report)), "1 file reformatted, 1 file failed to reformat." - ) - self.assertEqual( - unstyle(str(report)), - "1 file reformatted, 1 file left unchanged, 1 file failed to reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 1 file left unchanged, 1 file failed to" - " reformat.", - ) - self.assertEqual( - unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", - ) - for i in (a,): - if ( - # Rule 1 - i % 2 == 0 - # Rule 2 - and i % 3 == 0 - ): - while ( - # Just a comment - call() - # Another - ): - print(i) - xxxxxxxxxxxxxxxx = Yyyy2YyyyyYyyyyy( - push_manager=context.request.resource_manager, - max_items_to_push=num_items, - batch_size=Yyyy2YyyyYyyyyYyyy.FULL_SIZE, - ).push( - # Only send the first n items. - items=items[:num_items] - ) - return ( - 'Utterly failed doctest test for %s\n File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) - ) - - def omitting_trailers(self) -> None: - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex] - get_collection( - hey_this_is_a_very_long_call, it_has_funny_attributes, really=True - )[OneLevelIndex][TwoLevelIndex][ThreeLevelIndex][FourLevelIndex] - d[0][1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20][21][ - 22 - ] - assignment = ( - some.rather.elaborate.rule() and another.rule.ending_with.index[123] - ) - - def easy_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - - def tricky_asserts(self) -> None: - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ), "Not what we expected" - - assert { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } == expected, ( - "Not what we expected and the message is too long to fit in one line" - ) - - assert expected( - value, is_going_to_be="too long to fit in a single line", srsly=True - ) == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, "Not what we expected" - - assert expected == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - }, ( - "Not what we expected and the message is too long to fit in one line" - " because it's too long" - ) - - dis_c_instance_method = """\ - %3d 0 LOAD_FAST 1 (x) - 2 LOAD_CONST 1 (1) - 4 COMPARE_OP 2 (==) - 6 LOAD_FAST 0 (self) - 8 STORE_ATTR 0 (x) - 10 LOAD_CONST 0 (None) - 12 RETURN_VALUE - """ % ( - _C.__init__.__code__.co_firstlineno + 1, - ) - - assert ( - expectedexpectedexpectedexpectedexpectedexpectedexpectedexpectedexpect - == { - key1: value1, - key2: value2, - key3: value3, - key4: value4, - key5: value5, - key6: value6, - key7: value7, - key8: value8, - key9: value9, - } - ) diff --git a/tests/data/docstring.py b/tests/data/docstring.py deleted file mode 100644 index 2d3d73a101c..00000000000 --- a/tests/data/docstring.py +++ /dev/null @@ -1,225 +0,0 @@ -class MyClass: - """ Multiline - class docstring - """ - - def method(self): - """Multiline - method docstring - """ - pass - - -def foo(): - """This is a docstring with - some lines of text here - """ - return - - -def bar(): - '''This is another docstring - with more lines of text - ''' - return - - -def baz(): - '''"This" is a string with some - embedded "quotes"''' - return - - -def troz(): - '''Indentation with tabs - is just as OK - ''' - return - - -def zort(): - """Another - multiline - docstring - """ - pass - -def poit(): - """ - Lorem ipsum dolor sit amet. - - Consectetur adipiscing elit: - - sed do eiusmod tempor incididunt ut labore - - dolore magna aliqua - - enim ad minim veniam - - quis nostrud exercitation ullamco laboris nisi - - aliquip ex ea commodo consequat - """ - pass - - -def under_indent(): - """ - These lines are indented in a way that does not -make sense. - """ - pass - - -def over_indent(): - """ - This has a shallow indent - - But some lines are deeper - - And the closing quote is too deep - """ - pass - - -def single_line(): - """But with a newline after it! - - """ - pass - - -def this(): - r""" - 'hey ho' - """ - - -def that(): - """ "hey yah" """ - - -def and_that(): - """ - "hey yah" """ - - -def and_this(): - ''' - "hey yah"''' - - -def believe_it_or_not_this_is_in_the_py_stdlib(): ''' -"hey yah"''' - - -def ignored_docstring(): - """a => \ -b""" - -# output - -class MyClass: - """Multiline - class docstring - """ - - def method(self): - """Multiline - method docstring - """ - pass - - -def foo(): - """This is a docstring with - some lines of text here - """ - return - - -def bar(): - """This is another docstring - with more lines of text - """ - return - - -def baz(): - '''"This" is a string with some - embedded "quotes"''' - return - - -def troz(): - """Indentation with tabs - is just as OK - """ - return - - -def zort(): - """Another - multiline - docstring - """ - pass - - -def poit(): - """ - Lorem ipsum dolor sit amet. - - Consectetur adipiscing elit: - - sed do eiusmod tempor incididunt ut labore - - dolore magna aliqua - - enim ad minim veniam - - quis nostrud exercitation ullamco laboris nisi - - aliquip ex ea commodo consequat - """ - pass - - -def under_indent(): - """ - These lines are indented in a way that does not - make sense. - """ - pass - - -def over_indent(): - """ - This has a shallow indent - - But some lines are deeper - - And the closing quote is too deep - """ - pass - - -def single_line(): - """But with a newline after it!""" - pass - - -def this(): - r""" - 'hey ho' - """ - - -def that(): - """ "hey yah" """ - - -def and_that(): - """ - "hey yah" """ - - -def and_this(): - ''' - "hey yah"''' - - -def believe_it_or_not_this_is_in_the_py_stdlib(): - ''' - "hey yah"''' - - -def ignored_docstring(): - """a => \ -b""" \ No newline at end of file diff --git a/tests/data/function_trailing_comma.py b/tests/data/function_trailing_comma.py deleted file mode 100644 index d15459cbeb5..00000000000 --- a/tests/data/function_trailing_comma.py +++ /dev/null @@ -1,88 +0,0 @@ -def f(a,): - d = {'key': 'value',} - tup = (1,) - -def f2(a,b,): - d = {'key': 'value', 'key2': 'value2',} - tup = (1,2,) - -def f(a:int=1,): - call(arg={'explode': 'this',}) - call2(arg=[1,2,3],) - x = { - "a": 1, - "b": 2, - }["a"] - if a == {"a": 1,"b": 2,"c": 3,"d": 4,"e": 5,"f": 6,"g": 7,"h": 8,}["a"]: - pass - -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -]: - json = {"k": {"k2": {"k3": [1,]}}} - -# output - -def f( - a, -): - d = { - "key": "value", - } - tup = (1,) - - -def f2( - a, - b, -): - d = { - "key": "value", - "key2": "value2", - } - tup = ( - 1, - 2, - ) - - -def f( - a: int = 1, -): - call( - arg={ - "explode": "this", - } - ) - call2( - arg=[1, 2, 3], - ) - x = { - "a": 1, - "b": 2, - }["a"] - if a == { - "a": 1, - "b": 2, - "c": 3, - "d": 4, - "e": 5, - "f": 6, - "g": 7, - "h": 8, - }["a"]: - pass - - -def xxxxxxxxxxxxxxxxxxxxxxxxxxxx() -> Set[ - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -]: - json = { - "k": { - "k2": { - "k3": [ - 1, - ] - } - } - } \ No newline at end of file diff --git a/tests/data/long_strings_flag_disabled.py b/tests/data/long_strings_flag_disabled.py deleted file mode 100644 index ef3094fd779..00000000000 --- a/tests/data/long_strings_flag_disabled.py +++ /dev/null @@ -1,289 +0,0 @@ -x = "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." - -x += "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." - -y = "Short string" - -print( - "This is a really long string inside of a print statement with extra arguments attached at the end of it.", - x, - y, - z, -) - -print( - "This is a really long string inside of a print statement with no extra arguments attached at the end of it." -) - -D1 = { - "The First": "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", - "The Second": "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", -} - -D2 = { - 1.0: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", - 2.0: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", -} - -D3 = { - x: "This is a really long string that can't possibly be expected to fit all together on one line. Also it is inside a dictionary, so formatting is more difficult.", - y: "This is another really really (not really) long string that also can't be expected to fit on one line and is, like the other string, inside a dictionary.", -} - -D4 = { - "A long and ridiculous {}".format( - string_key - ): "This is a really really really long string that has to go i,side of a dictionary. It is soooo bad.", - some_func( - "calling", "some", "stuff" - ): "This is a really really really long string that has to go inside of a dictionary. It is {soooo} bad (#{x}).".format( - sooo="soooo", x=2 - ), - "A %s %s" - % ( - "formatted", - "string", - ): "This is a really really really long string that has to go inside of a dictionary. It is %s bad (#%d)." - % ("soooo", 2), -} - -func_with_keywords( - my_arg, - my_kwarg="Long keyword strings also need to be wrapped, but they will probably need to be handled a little bit differently.", -) - -bad_split1 = ( - "But what should happen when code has already been formatted but in the wrong way? Like" - " with a space at the end instead of the beginning. Or what about when it is split too soon?" -) - -bad_split2 = ( - "But what should happen when code has already " - "been formatted but in the wrong way? Like " - "with a space at the end instead of the " - "beginning. Or what about when it is split too " - "soon? In the case of a split that is too " - "short, black will try to honer the custom " - "split." -) - -bad_split3 = ( - "What if we have inline comments on " # First Comment - "each line of a bad split? In that " # Second Comment - "case, we should just leave it alone." # Third Comment -) - -bad_split_func1( - "But what should happen when code has already " - "been formatted but in the wrong way? Like " - "with a space at the end instead of the " - "beginning. Or what about when it is split too " - "soon? In the case of a split that is too " - "short, black will try to honer the custom " - "split.", - xxx, - yyy, - zzz, -) - -bad_split_func2( - xxx, - yyy, - zzz, - long_string_kwarg="But what should happen when code has already been formatted but in the wrong way? Like " - "with a space at the end instead of the beginning. Or what about when it is split too " - "soon?", -) - -bad_split_func3( - ( - "But what should happen when code has already " - r"been formatted but in the wrong way? Like " - "with a space at the end instead of the " - r"beginning. Or what about when it is split too " - r"soon? In the case of a split that is too " - "short, black will try to honer the custom " - "split." - ), - xxx, - yyy, - zzz, -) - -raw_string = r"This is a long raw string. When re-formatting this string, black needs to make sure it prepends the 'r' onto the new string." - -fmt_string1 = "We also need to be sure to preserve any and all {} which may or may not be attached to the string in question.".format( - "method calls" -) - -fmt_string2 = "But what about when the string is {} but {}".format( - "short", - "the method call is really really really really really really really really long?", -) - -old_fmt_string1 = ( - "While we are on the topic of %s, we should also note that old-style formatting must also be preserved, since some %s still uses it." - % ("formatting", "code") -) - -old_fmt_string2 = "This is a %s %s %s %s" % ( - "really really really really really", - "old", - "way to format strings!", - "Use f-strings instead!", -) - -old_fmt_string3 = "Whereas only the strings after the percent sign were long in the last example, this example uses a long initial string as well. This is another %s %s %s %s" % ( - "really really really really really", - "old", - "way to format strings!", - "Use f-strings instead!", -) - -fstring = f"f-strings definitely make things more {difficult} than they need to be for {{black}}. But boy they sure are handy. The problem is that some lines will need to have the 'f' whereas others do not. This {line}, for example, needs one." - -fstring_with_no_fexprs = f"Some regular string that needs to get split certainly but is NOT an fstring by any means whatsoever." - -comment_string = "Long lines with inline comments should have their comments appended to the reformatted string's enclosing right parentheses." # This comment gets thrown to the top. - -arg_comment_string = print( - "Long lines with inline comments which are apart of (and not the only member of) an argument list should have their comments appended to the reformatted string's enclosing left parentheses.", # This comment stays on the bottom. - "Arg #2", - "Arg #3", - "Arg #4", - "Arg #5", -) - -pragma_comment_string1 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa: E501 - -pragma_comment_string2 = "Lines which end with an inline pragma comment of the form `# : <...>` should be left alone." # noqa - -"""This is a really really really long triple quote string and it should not be touched.""" - -triple_quote_string = """This is a really really really long triple quote string assignment and it should not be touched.""" - -assert ( - some_type_of_boolean_expression -), "Followed by a really really really long string that is used to provide context to the AssertionError exception." - -assert ( - some_type_of_boolean_expression -), "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string {}.".format( - "formatting" -) - -assert some_type_of_boolean_expression, ( - "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic string %s." - % "formatting" -) - -assert some_type_of_boolean_expression, ( - "Followed by a really really really long string that is used to provide context to the AssertionError exception, which uses dynamic %s %s." - % ("string", "formatting") -) - -some_function_call( - "With a reallly generic name and with a really really long string that is, at some point down the line, " - + added - + " to a variable and then added to another string." -) - -some_function_call( - "With a reallly generic name and with a really really long string that is, at some point down the line, " - + added - + " to a variable and then added to another string. But then what happens when the final string is also supppppperrrrr long?! Well then that second (realllllllly long) string should be split too.", - "and a second argument", - and_a_third, -) - -return "A really really really really really really really really really really really really really long {} {}".format( - "return", "value" -) - -func_with_bad_comma( - "This is a really long string argument to a function that has a trailing comma which should NOT be there.", -) - -func_with_bad_comma( - "This is a really long string argument to a function that has a trailing comma which should NOT be there.", # comment after comma -) - -func_with_bad_comma( - ( - "This is a really long string argument to a function that has a trailing comma" - " which should NOT be there." - ), -) - -func_with_bad_comma( - ( - "This is a really long string argument to a function that has a trailing comma" - " which should NOT be there." - ), # comment after comma -) - -func_with_bad_parens_that_wont_fit_in_one_line( - ("short string that should have parens stripped"), x, y, z -) - -func_with_bad_parens_that_wont_fit_in_one_line( - x, y, ("short string that should have parens stripped"), z -) - -func_with_bad_parens( - ("short string that should have parens stripped"), - x, - y, - z, -) - -func_with_bad_parens( - x, - y, - ("short string that should have parens stripped"), - z, -) - -annotated_variable: Final = ( - "This is a large " - + STRING - + " that has been " - + CONCATENATED - + "using the '+' operator." -) -annotated_variable: Final = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." -annotated_variable: Literal[ - "fakse_literal" -] = "This is a large string that has a type annotation attached to it. A type annotation should NOT stop a long string from being wrapped." - -backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\" -backslashes = "This is a really long string with \"embedded\" double quotes and 'single' quotes that also handles checking for an even number of backslashes \\\\" -backslashes = "This is a really 'long' string with \"embedded double quotes\" and 'single' quotes that also handles checking for an odd number of backslashes \\\", like this...\\\\\\" - -short_string = "Hi" " there." - -func_call(short_string=("Hi" " there.")) - -raw_strings = r"Don't" " get" r" merged" " unless they are all raw." - - -def foo(): - yield "This is a really long string that can't possibly be expected to fit all together on one line. In fact it may even take up three or more lines... like four or five... but probably just three." - - -x = f"This is a {{really}} long string that needs to be split without a doubt (i.e. most definitely). In short, this {string} that can't possibly be {{expected}} to fit all together on one line. In {fact} it may even take up three or more lines... like four or five... but probably just four." - -long_unmergable_string_with_pragma = ( - "This is a really long string that can't be merged because it has a likely pragma at the end" # type: ignore - " of it." -) - -long_unmergable_string_with_pragma = ( - "This is a really long string that can't be merged because it has a likely pragma at the end" # noqa - " of it." -) - -long_unmergable_string_with_pragma = ( - "This is a really long string that can't be merged because it has a likely pragma at the end" # pylint: disable=some-pylint-check - " of it." -) diff --git a/tests/data/miscellaneous/invalid_header.py b/tests/data/miscellaneous/invalid_header.py new file mode 100644 index 00000000000..fb49e2f93e7 --- /dev/null +++ b/tests/data/miscellaneous/invalid_header.py @@ -0,0 +1,2 @@ +This is not valid Python syntax +y = "This is valid syntax" diff --git a/tests/data/percent_precedence.py b/tests/data/percent_precedence.py deleted file mode 100644 index b895443fb46..00000000000 --- a/tests/data/percent_precedence.py +++ /dev/null @@ -1,41 +0,0 @@ -("" % a) ** 2 -("" % a)[0] -("" % a)() -("" % a).b - -2 * ("" % a) -2 @ ("" % a) -2 / ("" % a) -2 // ("" % a) -2 % ("" % a) -+("" % a) -b + ("" % a) --("" % a) -b - ("" % a) -b + -("" % a) -~("" % a) -2 ** ("" % a) -await ("" % a) -b[("" % a)] -b(("" % a)) -# output -("" % a) ** 2 -("" % a)[0] -("" % a)() -("" % a).b - -2 * ("" % a) -2 @ ("" % a) -2 / ("" % a) -2 // ("" % a) -2 % ("" % a) -+("" % a) -b + "" % a --("" % a) -b - "" % a -b + -("" % a) -~("" % a) -2 ** ("" % a) -await ("" % a) -b[("" % a)] -b(("" % a)) diff --git a/tests/data/preview/cantfit.py b/tests/data/preview/cantfit.py index 0849374f776..cade382e30d 100644 --- a/tests/data/preview/cantfit.py +++ b/tests/data/preview/cantfit.py @@ -79,10 +79,14 @@ ) # long arguments normal_name = normal_function_name( - "but with super long string arguments that on their own exceed the line limit so" - " there's no way it can ever fit", - "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" - " with spam and eggs and spam with eggs", + ( + "but with super long string arguments that on their own exceed the line limit" + " so there's no way it can ever fit" + ), + ( + "eggs with spam and eggs and spam with eggs with spam and eggs and spam with" + " eggs with spam and eggs and spam with eggs" + ), this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, ) string_variable_name = "a string that is waaaaaaaayyyyyyyy too long, even in parens, there's nothing you can do" # noqa diff --git a/tests/data/preview/comments9.py b/tests/data/preview/comments9.py new file mode 100644 index 00000000000..449612c037a --- /dev/null +++ b/tests/data/preview/comments9.py @@ -0,0 +1,254 @@ +# Test for https://github.com/psf/black/issues/246. + +some = statement +# This comment should be split from the statement above by two lines. +def function(): + pass + + +some = statement +# This multiline comments section +# should be split from the statement +# above by two lines. +def function(): + pass + + +some = statement +# This comment should be split from the statement above by two lines. +async def async_function(): + pass + + +some = statement +# This comment should be split from the statement above by two lines. +class MyClass: + pass + + +some = statement +# This should be stick to the statement above + +# This should be split from the above by two lines +class MyClassWithComplexLeadingComments: + pass + + +class ClassWithDocstring: + """A docstring.""" +# Leading comment after a class with just a docstring +class MyClassAfterAnotherClassWithDocstring: + pass + + +some = statement +# leading 1 +@deco1 +# leading 2 +# leading 2 extra +@deco2(with_args=True) +# leading 3 +@deco3 +# leading 4 +def decorated(): + pass + + +some = statement +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) + +# leading 3 that already has an empty line +@deco3 +# leading 4 +def decorated_with_split_leading_comments(): + pass + + +some = statement +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) +# leading 3 +@deco3 + +# leading 4 that already has an empty line +def decorated_with_split_leading_comments(): + pass + + +def main(): + if a: + # Leading comment before inline function + def inline(): + pass + # Another leading comment + def another_inline(): + pass + else: + # More leading comments + def inline_after_else(): + pass + + +if a: + # Leading comment before "top-level inline" function + def top_level_quote_inline(): + pass + # Another leading comment + def another_top_level_quote_inline_inline(): + pass +else: + # More leading comments + def top_level_quote_inline_after_else(): + pass + + +class MyClass: + # First method has no empty lines between bare class def. + # More comments. + def first_method(self): + pass + + +# output + + +# Test for https://github.com/psf/black/issues/246. + +some = statement + + +# This comment should be split from the statement above by two lines. +def function(): + pass + + +some = statement + + +# This multiline comments section +# should be split from the statement +# above by two lines. +def function(): + pass + + +some = statement + + +# This comment should be split from the statement above by two lines. +async def async_function(): + pass + + +some = statement + + +# This comment should be split from the statement above by two lines. +class MyClass: + pass + + +some = statement +# This should be stick to the statement above + + +# This should be split from the above by two lines +class MyClassWithComplexLeadingComments: + pass + + +class ClassWithDocstring: + """A docstring.""" + + +# Leading comment after a class with just a docstring +class MyClassAfterAnotherClassWithDocstring: + pass + + +some = statement + + +# leading 1 +@deco1 +# leading 2 +# leading 2 extra +@deco2(with_args=True) +# leading 3 +@deco3 +# leading 4 +def decorated(): + pass + + +some = statement + + +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) + +# leading 3 that already has an empty line +@deco3 +# leading 4 +def decorated_with_split_leading_comments(): + pass + + +some = statement + + +# leading 1 +@deco1 +# leading 2 +@deco2(with_args=True) +# leading 3 +@deco3 + +# leading 4 that already has an empty line +def decorated_with_split_leading_comments(): + pass + + +def main(): + if a: + # Leading comment before inline function + def inline(): + pass + + # Another leading comment + def another_inline(): + pass + + else: + # More leading comments + def inline_after_else(): + pass + + +if a: + # Leading comment before "top-level inline" function + def top_level_quote_inline(): + pass + + # Another leading comment + def another_top_level_quote_inline_inline(): + pass + +else: + # More leading comments + def top_level_quote_inline_after_else(): + pass + + +class MyClass: + # First method has no empty lines between bare class def. + # More comments. + def first_method(self): + pass diff --git a/tests/data/preview/long_strings.py b/tests/data/preview/long_strings.py index 6db3cfed9a9..9288b253b60 100644 --- a/tests/data/preview/long_strings.py +++ b/tests/data/preview/long_strings.py @@ -297,8 +297,10 @@ def foo(): y = "Short string" print( - "This is a really long string inside of a print statement with extra arguments" - " attached at the end of it.", + ( + "This is a really long string inside of a print statement with extra arguments" + " attached at the end of it." + ), x, y, z, @@ -474,13 +476,15 @@ def foo(): ) bad_split_func1( - "But what should happen when code has already " - "been formatted but in the wrong way? Like " - "with a space at the end instead of the " - "beginning. Or what about when it is split too " - "soon? In the case of a split that is too " - "short, black will try to honer the custom " - "split.", + ( + "But what should happen when code has already " + "been formatted but in the wrong way? Like " + "with a space at the end instead of the " + "beginning. Or what about when it is split too " + "soon? In the case of a split that is too " + "short, black will try to honer the custom " + "split." + ), xxx, yyy, zzz, @@ -583,9 +587,11 @@ def foo(): ) arg_comment_string = print( - "Long lines with inline comments which are apart of (and not the only member of) an" - " argument list should have their comments appended to the reformatted string's" - " enclosing left parentheses.", # This comment gets thrown to the top. + ( # This comment gets thrown to the top. + "Long lines with inline comments which are apart of (and not the only member" + " of) an argument list should have their comments appended to the reformatted" + " string's enclosing left parentheses." + ), "Arg #2", "Arg #3", "Arg #4", @@ -645,23 +651,31 @@ def foo(): ) func_with_bad_comma( - "This is a really long string argument to a function that has a trailing comma" - " which should NOT be there.", + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), ) func_with_bad_comma( - "This is a really long string argument to a function that has a trailing comma" - " which should NOT be there.", # comment after comma + ( # comment after comma + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), ) func_with_bad_comma( - "This is a really long string argument to a function that has a trailing comma" - " which should NOT be there.", + ( + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), ) func_with_bad_comma( - "This is a really long string argument to a function that has a trailing comma" - " which should NOT be there.", # comment after comma + ( # comment after comma + "This is a really long string argument to a function that has a trailing comma" + " which should NOT be there." + ), ) func_with_bad_parens_that_wont_fit_in_one_line( diff --git a/tests/data/preview/long_strings__regression.py b/tests/data/preview/long_strings__regression.py index 634db46a5e0..8b00e76f40e 100644 --- a/tests/data/preview/long_strings__regression.py +++ b/tests/data/preview/long_strings__regression.py @@ -679,9 +679,11 @@ class A: def foo(): some_func_call( "xxxxxxxxxx", - "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " - '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; ' - "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + '"xxxx xxxxxxx xxxxxx xxxx; xxxx xxxxxx_xxxxx xxxxxx xxxx; ' + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), None, ("xxxxxxxxxxx",), ), @@ -690,9 +692,11 @@ def foo(): class A: def foo(): some_func_call( - "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " - "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " - "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" ", + ( + "xx {xxxxxxxxxxx}/xxxxxxxxxxx.xxx xxxx.xxx && xxxxxx -x " + "xxxx, ('xxxxxxx xxxxxx xxxx, xxxx') xxxxxx_xxxxx xxxxxx xxxx; " + "xxxx.xxxx_xxxxxx(['xxxx.xxx'], xxxx.xxxxxxx().xxxxxxxxxx)\" " + ), None, ("xxxxxxxxxxx",), ), @@ -810,8 +814,10 @@ def foo(): ) lpar_and_rpar_have_comments = func_call( # LPAR Comment - "Long really ridiculous type of string that shouldn't really even exist at all. I" - " mean commmme onnn!!!", # Comma Comment + ( # Comma Comment + "Long really ridiculous type of string that shouldn't really even exist at all." + " I mean commmme onnn!!!" + ), ) # RPAR Comment cmd_fstring = ( diff --git a/tests/data/preview/remove_await_parens.py b/tests/data/preview/remove_await_parens.py index eb7dad340c3..571210a2d80 100644 --- a/tests/data/preview/remove_await_parens.py +++ b/tests/data/preview/remove_await_parens.py @@ -80,6 +80,7 @@ async def main(): # output import asyncio + # Control example async def main(): await asyncio.sleep(1) diff --git a/tests/data/py_310/pep_572_py310.py b/tests/data/py_310/pep_572_py310.py index 2aef589ce8d..cb82b2d23f8 100644 --- a/tests/data/py_310/pep_572_py310.py +++ b/tests/data/py_310/pep_572_py310.py @@ -2,3 +2,14 @@ x[a:=0] x[a:=0, b:=1] x[5, b:=0] + +# Walruses are allowed inside generator expressions on function calls since 3.10. +if any(match := pattern_error.match(s) for s in buffer): + if match.group(2) == data_not_available: + # Error OK to ignore. + pass + +f(a := b + c for c in range(10)) +f((a := b + c for c in range(10)), x) +f(y=(a := b + c for c in range(10))) +f(x, (a := b + c for c in range(10)), y=z, **q) diff --git a/tests/data/simple_cases/comments5.py b/tests/data/simple_cases/comments5.py index d83b6b8ff47..c8c38813d55 100644 --- a/tests/data/simple_cases/comments5.py +++ b/tests/data/simple_cases/comments5.py @@ -58,9 +58,9 @@ def decorated1(): ... -# Note: crappy but inevitable. The current design of EmptyLineTracker doesn't -# allow this to work correctly. The user will have to split those lines by -# hand. +# Note: this is fixed in +# Preview.empty_lines_before_class_or_def_with_leading_comments. +# In the current style, the user will have to split those lines by hand. some_instruction # This comment should be split from `some_instruction` by two lines but isn't. def g(): diff --git a/tests/data/simple_cases/docstring_no_extra_empty_line_before_eof.py b/tests/data/simple_cases/docstring_no_extra_empty_line_before_eof.py new file mode 100644 index 00000000000..6fea860adf6 --- /dev/null +++ b/tests/data/simple_cases/docstring_no_extra_empty_line_before_eof.py @@ -0,0 +1,4 @@ +# Make sure when the file ends with class's docstring, +# It doesn't add extra blank lines. +class ClassWithDocstring: + """A docstring.""" diff --git a/tests/data/trailing_comma_optional_parens1.py b/tests/data/trailing_comma_optional_parens1.py deleted file mode 100644 index 5ad29a8affd..00000000000 --- a/tests/data/trailing_comma_optional_parens1.py +++ /dev/null @@ -1,3 +0,0 @@ -if e1234123412341234.winerror not in (_winapi.ERROR_SEM_TIMEOUT, - _winapi.ERROR_PIPE_BUSY) or _check_timeout(t): - pass \ No newline at end of file diff --git a/tests/data/trailing_comma_optional_parens2.py b/tests/data/trailing_comma_optional_parens2.py deleted file mode 100644 index 2817073816e..00000000000 --- a/tests/data/trailing_comma_optional_parens2.py +++ /dev/null @@ -1,3 +0,0 @@ -if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or - (8, 5, 8) <= get_tk_patchlevel() < (8, 6)): - pass \ No newline at end of file diff --git a/tests/data/trailing_comma_optional_parens3.py b/tests/data/trailing_comma_optional_parens3.py deleted file mode 100644 index e6a673ec537..00000000000 --- a/tests/data/trailing_comma_optional_parens3.py +++ /dev/null @@ -1,8 +0,0 @@ -if True: - if True: - if True: - return _( - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas " - + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.", - "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe", - ) % {"reported_username": reported_username, "report_reason": report_reason} \ No newline at end of file diff --git a/tests/test_black.py b/tests/test_black.py index 7f85fcdc409..9256698ea27 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -232,7 +232,7 @@ def _test_wip(self) -> None: sys.settrace(None) self.assertFormatEqual(expected, actual) black.assert_equivalent(source, actual) - black.assert_stable(source, actual, DEFAULT_MODE) + black.assert_stable(source, actual, black.FileMode()) def test_pep_572_version_detection(self) -> None: source, _ = read_data("py_38", "pep_572") @@ -341,6 +341,30 @@ def test_string_quotes(self) -> None: black.assert_equivalent(source, not_normalized) black.assert_stable(source, not_normalized, mode=mode) + def test_skip_source_first_line(self) -> None: + source, _ = read_data("miscellaneous", "invalid_header") + tmp_file = Path(black.dump_to_file(source)) + # Full source should fail (invalid syntax at header) + self.invokeBlack([str(tmp_file), "--diff", "--check"], exit_code=123) + # So, skipping the first line should work + result = BlackRunner().invoke( + black.main, [str(tmp_file), "-x", f"--config={EMPTY_CONFIG}"] + ) + self.assertEqual(result.exit_code, 0) + with open(tmp_file, encoding="utf8") as f: + actual = f.read() + self.assertFormatEqual(source, actual) + + def test_skip_source_first_line_when_mixing_newlines(self) -> None: + code_mixing_newlines = b"Header will be skipped\r\ni = [1,2,3]\nj = [1,2,3]\n" + expected = b"Header will be skipped\r\ni = [1, 2, 3]\nj = [1, 2, 3]\n" + with TemporaryDirectory() as workspace: + test_file = Path(workspace) / "skip_header.py" + test_file.write_bytes(code_mixing_newlines) + mode = replace(DEFAULT_MODE, skip_source_first_line=True) + ff(test_file, mode=mode, write_back=black.WriteBack.YES) + self.assertEqual(test_file.read_bytes(), expected) + def test_skip_magic_trailing_comma(self) -> None: source, _ = read_data("simple_cases", "expression") expected, _ = read_data( @@ -466,8 +490,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e1: boom") self.assertEqual( unstyle(str(report)), - "1 file reformatted, 2 files left unchanged, 1 file failed to" - " reformat.", + ( + "1 file reformatted, 2 files left unchanged, 1 file failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f3"), black.Changed.YES) @@ -476,8 +502,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "reformatted f3") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 1 file failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 1 file failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.failed(Path("e2"), "boom") @@ -486,8 +514,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e2: boom") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.path_ignored(Path("wat"), "no match") @@ -496,8 +526,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "wat ignored: no match") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f4"), black.Changed.NO) @@ -506,22 +538,28 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "f4 already well formatted, good job.") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 3 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 3 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.check = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, 2" - " files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2" + " files would fail to reformat." + ), ) report.check = False report.diff = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, 2" - " files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2" + " files would fail to reformat." + ), ) def test_report_quiet(self) -> None: @@ -563,8 +601,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e1: boom") self.assertEqual( unstyle(str(report)), - "1 file reformatted, 2 files left unchanged, 1 file failed to" - " reformat.", + ( + "1 file reformatted, 2 files left unchanged, 1 file failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f3"), black.Changed.YES) @@ -572,8 +612,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 1) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 1 file failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 1 file failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.failed(Path("e2"), "boom") @@ -582,8 +624,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e2: boom") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.path_ignored(Path("wat"), "no match") @@ -591,8 +635,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f4"), black.Changed.NO) @@ -600,22 +646,28 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 3 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 3 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.check = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, 2" - " files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2" + " files would fail to reformat." + ), ) report.check = False report.diff = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, 2" - " files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2" + " files would fail to reformat." + ), ) def test_report_normal(self) -> None: @@ -659,8 +711,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e1: boom") self.assertEqual( unstyle(str(report)), - "1 file reformatted, 2 files left unchanged, 1 file failed to" - " reformat.", + ( + "1 file reformatted, 2 files left unchanged, 1 file failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f3"), black.Changed.YES) @@ -669,8 +723,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(out_lines[-1], "reformatted f3") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 1 file failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 1 file failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.failed(Path("e2"), "boom") @@ -679,8 +735,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(err_lines[-1], "error: cannot format e2: boom") self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.path_ignored(Path("wat"), "no match") @@ -688,8 +746,10 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 2 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 2 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.done(Path("f4"), black.Changed.NO) @@ -697,22 +757,28 @@ def err(msg: str, **kwargs: Any) -> None: self.assertEqual(len(err_lines), 2) self.assertEqual( unstyle(str(report)), - "2 files reformatted, 3 files left unchanged, 2 files failed to" - " reformat.", + ( + "2 files reformatted, 3 files left unchanged, 2 files failed to" + " reformat." + ), ) self.assertEqual(report.return_code, 123) report.check = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, 2" - " files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2" + " files would fail to reformat." + ), ) report.check = False report.diff = True self.assertEqual( unstyle(str(report)), - "2 files would be reformatted, 3 files would be left unchanged, 2" - " files would fail to reformat.", + ( + "2 files would be reformatted, 3 files would be left unchanged, 2" + " files would fail to reformat." + ), ) def test_lib2to3_parse(self) -> None: diff --git a/tests/test_blackd.py b/tests/test_blackd.py index db9a1652f8c..5b6461f7685 100644 --- a/tests/test_blackd.py +++ b/tests/test_blackd.py @@ -177,6 +177,20 @@ async def test_blackd_invalid_line_length(self) -> None: ) self.assertEqual(response.status, 400) + @unittest_run_loop + async def test_blackd_skip_first_source_line(self) -> None: + invalid_first_line = b"Header will be skipped\r\ni = [1,2,3]\nj = [1,2,3]\n" + expected_result = b"Header will be skipped\r\ni = [1, 2, 3]\nj = [1, 2, 3]\n" + response = await self.client.post("/", data=invalid_first_line) + self.assertEqual(response.status, 400) + response = await self.client.post( + "/", + data=invalid_first_line, + headers={blackd.SKIP_SOURCE_FIRST_LINE: "true"}, + ) + self.assertEqual(response.status, 200) + self.assertEqual(await response.read(), expected_result) + @unittest_run_loop async def test_blackd_preview(self) -> None: response = await self.client.post(