├── .flake8 ├── .git_archival.txt ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── docs-issue.md │ ├── feature_request.md │ └── style_issue.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── changelog.yml │ ├── diff_shades.yml │ ├── diff_shades_comment.yml │ ├── doc.yml │ ├── docker.yml │ ├── fuzz.yml │ ├── lint.yml │ ├── pypi_upload.yml │ ├── release_tests.yml │ ├── test.yml │ └── upload_binary.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .pre-commit-hooks.yaml ├── .prettierrc.yaml ├── .readthedocs.yaml ├── AUTHORS.md ├── CHANGES.md ├── CITATION.cff ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── SECURITY.md ├── action.yml ├── action └── main.py ├── autoload └── black.vim ├── docs ├── Makefile ├── _static │ ├── license.svg │ ├── logo2-readme.png │ ├── logo2.png │ └── pypi_template.svg ├── authors.md ├── change_log.md ├── compatible_configs │ ├── flake8 │ │ ├── .flake8 │ │ ├── setup.cfg │ │ └── tox.ini │ ├── isort │ │ ├── .editorconfig │ │ ├── .isort.cfg │ │ ├── pyproject.toml │ │ └── setup.cfg │ ├── pycodestyle │ │ ├── .flake8 │ │ ├── setup.cfg │ │ └── tox.ini │ └── pylint │ │ ├── pylintrc │ │ ├── pyproject.toml │ │ └── setup.cfg ├── conf.py ├── contributing │ ├── gauging_changes.md │ ├── index.md │ ├── issue_triage.md │ ├── release_process.md │ └── the_basics.md ├── faq.md ├── getting_started.md ├── guides │ ├── index.md │ ├── introducing_black_to_your_project.md │ └── using_black_with_other_tools.md ├── index.md ├── integrations │ ├── editors.md │ ├── github_actions.md │ ├── index.md │ └── source_version_control.md ├── license.md ├── make.bat ├── requirements.txt ├── the_black_code_style │ ├── current_style.md │ ├── future_style.md │ └── index.md └── usage_and_configuration │ ├── black_as_a_server.md │ ├── black_docker_image.md │ ├── file_collection_and_discovery.md │ ├── index.md │ └── the_basics.md ├── gallery ├── Dockerfile ├── README.md └── gallery.py ├── plugin └── black.vim ├── profiling ├── dict_big.py ├── dict_huge.py ├── list_big.py ├── list_huge.py ├── mix_big.py ├── mix_huge.py └── mix_small.py ├── pyproject.toml ├── scripts ├── __init__.py ├── check_pre_commit_rev_in_example.py ├── check_version_in_basics_example.py ├── diff_shades_gha_helper.py ├── fuzz.py ├── generate_schema.py ├── make_width_table.py ├── migrate-black.py ├── release.py └── release_tests.py ├── src ├── black │ ├── __init__.py │ ├── __main__.py │ ├── _width_table.py │ ├── brackets.py │ ├── cache.py │ ├── comments.py │ ├── concurrency.py │ ├── const.py │ ├── debug.py │ ├── files.py │ ├── handle_ipynb_magics.py │ ├── linegen.py │ ├── lines.py │ ├── mode.py │ ├── nodes.py │ ├── numerics.py │ ├── output.py │ ├── parsing.py │ ├── py.typed │ ├── ranges.py │ ├── report.py │ ├── resources │ │ ├── __init__.py │ │ └── black.schema.json │ ├── rusty.py │ ├── schema.py │ ├── strings.py │ └── trans.py ├── blackd │ ├── __init__.py │ ├── __main__.py │ └── middlewares.py └── blib2to3 │ ├── Grammar.txt │ ├── LICENSE │ ├── PatternGrammar.txt │ ├── README │ ├── __init__.py │ ├── pgen2 │ ├── __init__.py │ ├── conv.py │ ├── driver.py │ ├── grammar.py │ ├── literals.py │ ├── parse.py │ ├── pgen.py │ ├── token.py │ └── tokenize.py │ ├── pygram.py │ └── pytree.py ├── test_requirements.txt ├── tests ├── __init__.py ├── conftest.py ├── data │ ├── cases │ │ ├── allow_empty_first_line.py │ │ ├── annotations.py │ │ ├── async_stmts.py │ │ ├── attribute_access_on_number_literals.py │ │ ├── backslash_before_indent.py │ │ ├── beginning_backslash.py │ │ ├── bracketmatch.py │ │ ├── bytes_docstring.py │ │ ├── cantfit.py │ │ ├── class_blank_parentheses.py │ │ ├── class_methods_new_line.py │ │ ├── collections.py │ │ ├── comment_after_escaped_newline.py │ │ ├── comment_type_hint.py │ │ ├── comments.py │ │ ├── comments2.py │ │ ├── comments3.py │ │ ├── comments4.py │ │ ├── comments5.py │ │ ├── comments6.py │ │ ├── comments8.py │ │ ├── comments9.py │ │ ├── comments_in_blocks.py │ │ ├── comments_in_double_parens.py │ │ ├── comments_non_breaking_space.py │ │ ├── composition.py │ │ ├── composition_no_trailing_comma.py │ │ ├── conditional_expression.py │ │ ├── context_managers_38.py │ │ ├── context_managers_39.py │ │ ├── context_managers_autodetect_310.py │ │ ├── context_managers_autodetect_311.py │ │ ├── context_managers_autodetect_38.py │ │ ├── context_managers_autodetect_39.py │ │ ├── docstring.py │ │ ├── docstring_newline.py │ │ ├── docstring_no_extra_empty_line_before_eof.py │ │ ├── docstring_no_string_normalization.py │ │ ├── docstring_preview.py │ │ ├── dummy_implementations.py │ │ ├── empty_lines.py │ │ ├── expression.diff │ │ ├── expression.py │ │ ├── f_docstring.py │ │ ├── fmtonoff.py │ │ ├── fmtonoff2.py │ │ ├── fmtonoff3.py │ │ ├── fmtonoff4.py │ │ ├── fmtonoff5.py │ │ ├── fmtonoff6.py │ │ ├── fmtpass_imports.py │ │ ├── fmtskip.py │ │ ├── fmtskip10.py │ │ ├── fmtskip11.py │ │ ├── fmtskip2.py │ │ ├── fmtskip3.py │ │ ├── fmtskip4.py │ │ ├── fmtskip5.py │ │ ├── fmtskip6.py │ │ ├── fmtskip7.py │ │ ├── fmtskip8.py │ │ ├── fmtskip9.py │ │ ├── form_feeds.py │ │ ├── format_unicode_escape_seq.py │ │ ├── fstring.py │ │ ├── fstring_quotations.py │ │ ├── funcdef_return_type_trailing_comma.py │ │ ├── function.py │ │ ├── function2.py │ │ ├── function_trailing_comma.py │ │ ├── generics_wrapping.py │ │ ├── ignore_pyi.py │ │ ├── import_spacing.py │ │ ├── is_simple_lookup_for_doublestar_expression.py │ │ ├── keep_newline_after_match.py │ │ ├── line_ranges_basic.py │ │ ├── line_ranges_diff_edge_case.py │ │ ├── line_ranges_exceeding_end.py │ │ ├── line_ranges_fmt_off.py │ │ ├── line_ranges_fmt_off_decorator.py │ │ ├── line_ranges_fmt_off_overlap.py │ │ ├── line_ranges_imports.py │ │ ├── line_ranges_indentation.py │ │ ├── line_ranges_outside_source.py │ │ ├── line_ranges_two_passes.py │ │ ├── line_ranges_unwrapping.py │ │ ├── linelength6.py │ │ ├── long_strings__type_annotations.py │ │ ├── long_strings_flag_disabled.py │ │ ├── module_docstring_1.py │ │ ├── module_docstring_2.py │ │ ├── module_docstring_3.py │ │ ├── module_docstring_4.py │ │ ├── module_docstring_followed_by_class.py │ │ ├── module_docstring_followed_by_function.py │ │ ├── multiline_consecutive_open_parentheses_ignore.py │ │ ├── nested_stub.py │ │ ├── no_blank_line_before_docstring.py │ │ ├── numeric_literals.py │ │ ├── numeric_literals_skip_underscores.py │ │ ├── one_element_subscript.py │ │ ├── parenthesized_context_managers.py │ │ ├── pattern_matching_complex.py │ │ ├── pattern_matching_extras.py │ │ ├── pattern_matching_generic.py │ │ ├── pattern_matching_long.py │ │ ├── pattern_matching_simple.py │ │ ├── pattern_matching_style.py │ │ ├── pattern_matching_trailing_comma.py │ │ ├── pattern_matching_with_if_stmt.py │ │ ├── pep604_union_types_line_breaks.py │ │ ├── pep646_typed_star_arg_type_var_tuple.py │ │ ├── pep_570.py │ │ ├── pep_572.py │ │ ├── pep_572_do_not_remove_parens.py │ │ ├── pep_572_py310.py │ │ ├── pep_572_py39.py │ │ ├── pep_572_remove_parens.py │ │ ├── pep_572_slices.py │ │ ├── pep_604.py │ │ ├── pep_646.py │ │ ├── pep_654.py │ │ ├── pep_654_style.py │ │ ├── pep_701.py │ │ ├── percent_precedence.py │ │ ├── power_op_newline.py │ │ ├── power_op_spacing.py │ │ ├── power_op_spacing_long.py │ │ ├── prefer_rhs_split.py │ │ ├── prefer_rhs_split_reformatted.py │ │ ├── preview_cantfit_string.py │ │ ├── preview_comments7.py │ │ ├── preview_fstring.py │ │ ├── preview_hug_parens_with_braces_and_square_brackets.py │ │ ├── preview_hug_parens_with_braces_and_square_brackets_no_ll1.py │ │ ├── preview_import_line_collapse.py │ │ ├── preview_long_dict_values.py │ │ ├── preview_long_strings.py │ │ ├── preview_long_strings__east_asian_width.py │ │ ├── preview_long_strings__edge_case.py │ │ ├── preview_long_strings__regression.py │ │ ├── preview_multiline_strings.py │ │ ├── preview_remove_multiline_lone_list_item_parens.py │ │ ├── preview_return_annotation_brackets_string.py │ │ ├── py310_pep572.py │ │ ├── python37.py │ │ ├── python38.py │ │ ├── python39.py │ │ ├── raw_docstring.py │ │ ├── raw_docstring_no_string_normalization.py │ │ ├── remove_await_parens.py │ │ ├── remove_except_parens.py │ │ ├── remove_for_brackets.py │ │ ├── remove_lone_list_item_parens.py │ │ ├── remove_newline_after_code_block_open.py │ │ ├── remove_parens.py │ │ ├── remove_redundant_parens_in_case_guard.py │ │ ├── remove_with_brackets.py │ │ ├── return_annotation_brackets.py │ │ ├── single_line_format_skip_with_multiple_comments.py │ │ ├── skip_magic_trailing_comma.py │ │ ├── skip_magic_trailing_comma_generic_wrap.py │ │ ├── slices.py │ │ ├── split_delimiter_comments.py │ │ ├── starred_for_target.py │ │ ├── string_prefixes.py │ │ ├── stub.py │ │ ├── torture.py │ │ ├── trailing_comma.py │ │ ├── trailing_comma_optional_parens1.py │ │ ├── trailing_comma_optional_parens2.py │ │ ├── trailing_comma_optional_parens3.py │ │ ├── trailing_commas_in_leading_parts.py │ │ ├── tricky_unicode_symbols.py │ │ ├── tupleassign.py │ │ ├── type_aliases.py │ │ ├── type_comment_syntax_error.py │ │ ├── type_param_defaults.py │ │ ├── type_params.py │ │ ├── typed_params_trailing_comma.py │ │ ├── walrus_in_dict.py │ │ └── whitespace.py │ ├── empty_pyproject.toml │ ├── gitignore_used_on_multiple_sources │ │ ├── .gitignore │ │ ├── dir1 │ │ │ ├── a.py │ │ │ └── b.py │ │ └── dir2 │ │ │ ├── a.py │ │ │ └── b.py │ ├── ignore_directory_gitignore_tests │ │ ├── .gitignore │ │ ├── abc.py │ │ ├── large_ignored_dir_two │ │ │ ├── a.py │ │ │ ├── inner │ │ │ │ └── b.py │ │ │ ├── inner2 │ │ │ │ └── c.py │ │ │ └── inner3 │ │ │ │ └── d.py │ │ └── z.py │ ├── ignore_subfolders_gitignore_tests │ │ ├── a.py │ │ └── subdir │ │ │ ├── .gitignore │ │ │ ├── b.py │ │ │ └── subdir │ │ │ └── c.py │ ├── include_exclude_tests │ │ ├── .gitignore │ │ ├── b │ │ │ ├── .definitely_exclude │ │ │ │ ├── a.pie │ │ │ │ ├── a.py │ │ │ │ └── a.pyi │ │ │ ├── dont_exclude │ │ │ │ ├── a.pie │ │ │ │ ├── a.py │ │ │ │ └── a.pyi │ │ │ └── exclude │ │ │ │ ├── a.pie │ │ │ │ ├── a.py │ │ │ │ └── a.pyi │ │ └── pyproject.toml │ ├── incorrect_spelling.toml │ ├── invalid_gitignore_tests │ │ ├── .gitignore │ │ ├── a.py │ │ └── pyproject.toml │ ├── invalid_line_ranges.toml │ ├── invalid_nested_gitignore_tests │ │ ├── a.py │ │ ├── a │ │ │ ├── .gitignore │ │ │ └── a.py │ │ └── pyproject.toml │ ├── jupyter │ │ ├── non_python_notebook.ipynb │ │ ├── notebook_empty_metadata.ipynb │ │ ├── notebook_no_trailing_newline.ipynb │ │ ├── notebook_trailing_newline.ipynb │ │ ├── notebook_which_cant_be_parsed.ipynb │ │ └── notebook_without_changes.ipynb │ ├── line_ranges_formatted │ │ ├── basic.py │ │ └── pattern_matching.py │ ├── miscellaneous │ │ ├── async_as_identifier.py │ │ ├── blackd_diff.diff │ │ ├── blackd_diff.py │ │ ├── debug_visitor.out │ │ ├── debug_visitor.py │ │ ├── decorators.py │ │ ├── expression_skip_magic_trailing_comma.diff │ │ ├── force_py36.py │ │ ├── force_pyi.py │ │ ├── invalid_header.py │ │ ├── missing_final_newline.diff │ │ ├── missing_final_newline.py │ │ ├── pattern_matching_invalid.py │ │ ├── python2_detection.py │ │ └── string_quotes.py │ ├── nested_gitignore_tests │ │ ├── pyproject.toml │ │ ├── root │ │ │ ├── .gitignore │ │ │ ├── a.py │ │ │ ├── b.py │ │ │ ├── c.py │ │ │ └── child │ │ │ │ ├── .gitignore │ │ │ │ ├── a.py │ │ │ │ ├── b.py │ │ │ │ └── c.py │ │ └── x.py │ └── project_metadata │ │ ├── both_pyproject.toml │ │ ├── neither_pyproject.toml │ │ ├── only_black_pyproject.toml │ │ └── only_metadata_pyproject.toml ├── empty.toml ├── optional.py ├── test.toml ├── test_black.py ├── test_blackd.py ├── test_docs.py ├── test_format.py ├── test_ipynb.py ├── test_no_ipynb.py ├── test_ranges.py ├── test_schema.py ├── test_tokenize.py ├── test_trans.py └── util.py └── tox.ini /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | # B905 should be enabled when we drop support for 3.9 3 | ignore = E203, E266, E501, E701, E704, W503, B905, B907 4 | # line length is intentionally set to 80 here because black uses Bugbear 5 | # See https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#bugbear for more details 6 | max-line-length = 80 7 | max-complexity = 18 8 | select = B,C,E,F,W,T4,B9 9 | -------------------------------------------------------------------------------- /.git_archival.txt: -------------------------------------------------------------------------------- 1 | node: e7bf7b4619928da69d486f36fcb456fb201ff53e 2 | node-date: 2025-05-29T14:10:29-07:00 3 | describe-name: 25.1.0-32-ge7bf7b46199 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .git_archival.txt export-subst 2 | *.py diff=python 3 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Treat each other well 2 | 3 | Everyone participating in the _Black_ project, and in particular in the issue tracker, 4 | pull requests, and social media activity, is expected to treat other people with respect 5 | and more generally to follow the guidelines articulated in the 6 | [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/). 7 | 8 | At the same time, humor is encouraged. In fact, basic familiarity with Monty Python's 9 | Flying Circus is expected. We are not savages. 10 | 11 | And if you _really_ need to slap somebody, do it with a fish while dancing. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve Black's quality 4 | title: "" 5 | labels: "T: bug" 6 | assignees: "" 7 | --- 8 | 9 | 26 | 27 | **Describe the bug** 28 | 29 | 30 | 31 | **To Reproduce** 32 | 33 | 36 | 37 | For example, take this code: 38 | 39 | ```python 40 | this = "code" 41 | ``` 42 | 43 | And run it with these arguments: 44 | 45 | ```sh 46 | $ black file.py --target-version py39 47 | ``` 48 | 49 | The resulting error is: 50 | 51 | > cannot format file.py: INTERNAL ERROR: ... 52 | 53 | **Expected behavior** 54 | 55 | 56 | 57 | **Environment** 58 | 59 | 60 | 61 | - Black's version: 62 | - OS and Python version: 63 | 64 | **Additional context** 65 | 66 | 67 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # See also: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser 2 | 3 | # This is the default and blank issues are useful so let's keep 'em. 4 | blank_issues_enabled: true 5 | 6 | contact_links: 7 | - name: Chat on Python Discord 8 | url: https://discord.gg/RtVdv86PrH 9 | about: | 10 | User support, questions, and other lightweight requests can be 11 | handled via the #black-formatter text channel we have on Python 12 | Discord. 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/docs-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation 3 | about: Report a problem with or suggest something for the documentation 4 | title: "" 5 | labels: "T: documentation" 6 | assignees: "" 7 | --- 8 | 9 | **Is this related to a problem? Please describe.** 10 | 11 | 13 | 14 | **Describe the solution you'd like** 15 | 16 | 18 | 19 | **Describe alternatives you've considered** 20 | 21 | 23 | 24 | **Additional context** 25 | 26 | 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "T: enhancement" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | 11 | 13 | 14 | **Describe the solution you'd like** 15 | 16 | 18 | 19 | **Describe alternatives you've considered** 20 | 21 | 23 | 24 | **Additional context** 25 | 26 | 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/style_issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Code style issue 3 | about: Help us improve the Black code style 4 | title: "" 5 | labels: "T: style" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the style change** 10 | 11 | 13 | 14 | **Examples in the current _Black_ style** 15 | 16 | 18 | 19 | ```python 20 | def f(): 21 | "Make sure this code is blackened""" 22 | pass 23 | ``` 24 | 25 | **Desired style** 26 | 27 | 28 | 29 | ```python 30 | def f( 31 | ): 32 | pass 33 | ``` 34 | 35 | **Additional context** 36 | 37 | 38 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 3 | 4 | ### Description 5 | 6 | 10 | 11 | ### Checklist - did you ... 12 | 13 | 20 | 21 | - [ ] Add an entry in `CHANGES.md` if necessary? 22 | - [ ] Add / update tests if necessary? 23 | - [ ] Add new / update outdated documentation? 24 | 25 | 37 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "github-actions" 6 | # Workflow files in .github/workflows will be checked 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | labels: ["skip news", "C: dependencies"] 11 | 12 | - package-ecosystem: "pip" 13 | directory: "docs/" 14 | schedule: 15 | interval: "weekly" 16 | labels: ["skip news", "C: dependencies", "T: documentation"] 17 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: changelog 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize, labeled, unlabeled, reopened] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | build: 12 | name: Changelog Entry Check 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Grep CHANGES.md for PR number 20 | if: contains(github.event.pull_request.labels.*.name, 'skip news') != true 21 | run: | 22 | grep -Pz "\((\n\s*)?#${{ github.event.pull_request.number }}(\n\s*)?\)" CHANGES.md || \ 23 | (echo "Please add '(#${{ github.event.pull_request.number }})' change line to CHANGES.md (or if appropriate, ask a maintainer to add the 'skip news' label)" && \ 24 | exit 1) 25 | -------------------------------------------------------------------------------- /.github/workflows/diff_shades_comment.yml: -------------------------------------------------------------------------------- 1 | name: diff-shades-comment 2 | 3 | on: 4 | workflow_run: 5 | workflows: [diff-shades] 6 | types: [completed] 7 | 8 | permissions: 9 | pull-requests: write 10 | 11 | jobs: 12 | comment: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-python@v5 17 | with: 18 | python-version: "*" 19 | 20 | - name: Install support dependencies 21 | run: | 22 | python -m pip install pip --upgrade 23 | python -m pip install click packaging urllib3 24 | 25 | - name: Get details from initial workflow run 26 | id: metadata 27 | env: 28 | GITHUB_TOKEN: ${{ github.token }} 29 | run: > 30 | python scripts/diff_shades_gha_helper.py comment-details 31 | ${{github.event.workflow_run.id }} 32 | 33 | - name: Try to find pre-existing PR comment 34 | if: steps.metadata.outputs.needs-comment == 'true' 35 | id: find-comment 36 | uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e 37 | with: 38 | issue-number: ${{ steps.metadata.outputs.pr-number }} 39 | comment-author: "github-actions[bot]" 40 | body-includes: "diff-shades" 41 | 42 | - name: Create or update PR comment 43 | if: steps.metadata.outputs.needs-comment == 'true' 44 | uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 45 | with: 46 | comment-id: ${{ steps.find-comment.outputs.comment-id }} 47 | issue-number: ${{ steps.metadata.outputs.pr-number }} 48 | body: ${{ steps.metadata.outputs.comment-body }} 49 | edit-mode: replace 50 | -------------------------------------------------------------------------------- /.github/workflows/doc.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | build: 10 | # We want to run on external PRs, but not on our own internal PRs as they'll be run 11 | # by the push to the branch. Without this if check, checks are duplicated since 12 | # internal PRs match both the push and pull_request events. 13 | if: 14 | github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 15 | github.repository 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | os: [ubuntu-latest, windows-latest] 21 | 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Set up latest Python 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: "3.13" 30 | allow-prereleases: true 31 | 32 | - name: Install dependencies 33 | run: | 34 | python -m pip install uv 35 | python -m uv venv 36 | python -m uv pip install -e ".[d]" 37 | python -m uv pip install -r "docs/requirements.txt" 38 | 39 | - name: Build documentation 40 | run: sphinx-build -a -b html -W --keep-going docs/ docs/_build 41 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: docker 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | release: 8 | types: [published] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | docker: 15 | if: github.repository == 'psf/black' 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up QEMU 22 | uses: docker/setup-qemu-action@v3 23 | 24 | - name: Set up Docker Buildx 25 | uses: docker/setup-buildx-action@v3 26 | 27 | - name: Login to DockerHub 28 | uses: docker/login-action@v3 29 | with: 30 | username: ${{ secrets.DOCKERHUB_USERNAME }} 31 | password: ${{ secrets.DOCKERHUB_TOKEN }} 32 | 33 | - name: Check + set version tag 34 | run: 35 | echo "GIT_TAG=$(git describe --candidates=0 --tags 2> /dev/null || echo 36 | latest_non_release)" >> $GITHUB_ENV 37 | 38 | - name: Build and push 39 | uses: docker/build-push-action@v6 40 | with: 41 | context: . 42 | platforms: linux/amd64,linux/arm64 43 | push: true 44 | tags: pyfound/black:latest,pyfound/black:${{ env.GIT_TAG }} 45 | 46 | - name: Build and push latest_release tag 47 | if: 48 | ${{ github.event_name == 'release' && github.event.action == 'published' && 49 | !github.event.release.prerelease }} 50 | uses: docker/build-push-action@v6 51 | with: 52 | context: . 53 | platforms: linux/amd64,linux/arm64 54 | push: true 55 | tags: pyfound/black:latest_release 56 | 57 | - name: Build and push latest_prerelease tag 58 | if: 59 | ${{ github.event_name == 'release' && github.event.action == 'published' && 60 | github.event.release.prerelease }} 61 | uses: docker/build-push-action@v6 62 | with: 63 | context: . 64 | platforms: linux/amd64,linux/arm64 65 | push: true 66 | tags: pyfound/black:latest_prerelease 67 | 68 | - name: Image digest 69 | run: echo ${{ steps.docker_build.outputs.digest }} 70 | -------------------------------------------------------------------------------- /.github/workflows/fuzz.yml: -------------------------------------------------------------------------------- 1 | name: Fuzz 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} 7 | cancel-in-progress: true 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | # We want to run on external PRs, but not on our own internal PRs as they'll be run 15 | # by the push to the branch. Without this if check, checks are duplicated since 16 | # internal PRs match both the push and pull_request events. 17 | if: 18 | github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 19 | github.repository 20 | 21 | runs-on: ubuntu-latest 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | python-version: ["3.9", "3.10", "3.11", "3.12.4", "3.13"] 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | 30 | - name: Set up Python ${{ matrix.python-version }} 31 | uses: actions/setup-python@v5 32 | with: 33 | python-version: ${{ matrix.python-version }} 34 | allow-prereleases: true 35 | 36 | - name: Install dependencies 37 | run: | 38 | python -m pip install --upgrade pip 39 | python -m pip install --upgrade tox 40 | 41 | - name: Run fuzz tests 42 | run: | 43 | tox -e fuzz 44 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint + format ourselves 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | # We want to run on external PRs, but not on our own internal PRs as they'll be run 8 | # by the push to the branch. Without this if check, checks are duplicated since 9 | # internal PRs match both the push and pull_request events. 10 | if: 11 | github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 12 | github.repository 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Assert PR target is main 20 | if: github.event_name == 'pull_request' && github.repository == 'psf/black' 21 | run: | 22 | if [ "$GITHUB_BASE_REF" != "main" ]; then 23 | echo "::error::PR targeting '$GITHUB_BASE_REF', please refile targeting 'main'." && exit 1 24 | fi 25 | 26 | - name: Set up latest Python 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: "3.13" 30 | allow-prereleases: true 31 | 32 | - name: Install dependencies 33 | run: | 34 | python -m pip install --upgrade pip 35 | python -m pip install -e '.' 36 | python -m pip install tox 37 | 38 | - name: Run pre-commit hooks 39 | uses: pre-commit/action@v3.0.1 40 | 41 | - name: Format ourselves 42 | run: | 43 | tox -e run_self 44 | 45 | - name: Regenerate schema 46 | run: | 47 | tox -e generate_schema 48 | git diff --exit-code 49 | -------------------------------------------------------------------------------- /.github/workflows/release_tests.yml: -------------------------------------------------------------------------------- 1 | name: Release tool CI 2 | 3 | on: 4 | push: 5 | paths: 6 | - .github/workflows/release_tests.yml 7 | - release.py 8 | - release_tests.py 9 | pull_request: 10 | paths: 11 | - .github/workflows/release_tests.yml 12 | - release.py 13 | - release_tests.py 14 | 15 | jobs: 16 | build: 17 | # We want to run on external PRs, but not on our own internal PRs as they'll be run 18 | # by the push to the branch. Without this if check, checks are duplicated since 19 | # internal PRs match both the push and pull_request events. 20 | if: 21 | github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 22 | github.repository 23 | 24 | name: Running python ${{ matrix.python-version }} on ${{matrix.os}} 25 | runs-on: ${{ matrix.os }} 26 | strategy: 27 | matrix: 28 | python-version: ["3.13"] 29 | os: [macOS-latest, ubuntu-latest, windows-latest] 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | with: 34 | # Give us all history, branches and tags 35 | fetch-depth: 0 36 | - name: Set up Python ${{ matrix.python-version }} 37 | uses: actions/setup-python@v5 38 | with: 39 | python-version: ${{ matrix.python-version }} 40 | allow-prereleases: true 41 | 42 | - name: Print Python Version 43 | run: python --version --version && which python 44 | 45 | - name: Print Git Version 46 | run: git --version && which git 47 | 48 | - name: Update pip, setuptools + wheels 49 | run: | 50 | python -m pip install --upgrade pip setuptools wheel 51 | 52 | - name: Run unit tests via coverage + print report 53 | run: | 54 | python -m pip install coverage 55 | coverage run scripts/release_tests.py 56 | coverage report --show-missing 57 | -------------------------------------------------------------------------------- /.github/workflows/upload_binary.yml: -------------------------------------------------------------------------------- 1 | name: Publish executables 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | permissions: 8 | contents: write # actions/upload-release-asset needs this. 9 | 10 | jobs: 11 | build: 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: [windows-2019, ubuntu-22.04, macos-latest] 17 | include: 18 | - os: windows-2019 19 | pathsep: ";" 20 | asset_name: black_windows.exe 21 | executable_mime: "application/vnd.microsoft.portable-executable" 22 | - os: ubuntu-22.04 23 | pathsep: ":" 24 | asset_name: black_linux 25 | executable_mime: "application/x-executable" 26 | - os: macos-latest 27 | pathsep: ":" 28 | asset_name: black_macos 29 | executable_mime: "application/x-mach-binary" 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | 34 | - name: Set up latest Python 35 | uses: actions/setup-python@v5 36 | with: 37 | python-version: "3.12.4" 38 | 39 | - name: Install Black and PyInstaller 40 | run: | 41 | python -m pip install --upgrade pip wheel 42 | python -m pip install .[colorama] 43 | python -m pip install pyinstaller 44 | 45 | - name: Build executable with PyInstaller 46 | run: > 47 | python -m PyInstaller -F --name ${{ matrix.asset_name }} --add-data 48 | 'src/blib2to3${{ matrix.pathsep }}blib2to3' src/black/__main__.py 49 | 50 | - name: Quickly test executable 51 | run: | 52 | ./dist/${{ matrix.asset_name }} --version 53 | ./dist/${{ matrix.asset_name }} src --verbose 54 | 55 | - name: Upload binary as release asset 56 | uses: actions/upload-release-asset@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | upload_url: ${{ github.event.release.upload_url }} 61 | asset_path: dist/${{ matrix.asset_name }} 62 | asset_name: ${{ matrix.asset_name }} 63 | asset_content_type: ${{ matrix.executable_mime }} 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | .coverage 3 | .coverage.* 4 | _build 5 | .DS_Store 6 | .vscode 7 | .python-version 8 | docs/_static/pypi.svg 9 | .tox 10 | __pycache__ 11 | 12 | # Packaging artifacts 13 | black.egg-info 14 | black.dist-info 15 | build/ 16 | dist/ 17 | pip-wheel-metadata/ 18 | .eggs 19 | 20 | src/_black_version.py 21 | .idea 22 | 23 | .dmypy.json 24 | *.swp 25 | .hypothesis/ 26 | venv/ 27 | .ipynb_checkpoints/ 28 | node_modules/ 29 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | # Note that we recommend using https://github.com/psf/black-pre-commit-mirror instead 2 | # This will work about 2x as fast as using the hooks in this repository 3 | - id: black 4 | name: black 5 | description: "Black: The uncompromising Python code formatter" 6 | entry: black 7 | language: python 8 | minimum_pre_commit_version: 2.9.2 9 | require_serial: true 10 | types_or: [python, pyi] 11 | - id: black-jupyter 12 | name: black-jupyter 13 | description: 14 | "Black: The uncompromising Python code formatter (with Jupyter Notebook support)" 15 | entry: black 16 | language: python 17 | minimum_pre_commit_version: 2.9.2 18 | require_serial: true 19 | types_or: [python, pyi, jupyter] 20 | additional_dependencies: [".[jupyter]"] 21 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | proseWrap: always 2 | printWidth: 88 3 | endOfLine: auto 4 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | formats: 4 | - htmlzip 5 | 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3.11" 10 | 11 | python: 12 | install: 13 | - requirements: docs/requirements.txt 14 | 15 | - method: pip 16 | path: . 17 | extra_requirements: 18 | - d 19 | 20 | sphinx: 21 | configuration: docs/conf.py 22 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: "Black: The uncompromising Python code formatter" 3 | message: >- 4 | If you use this software, please cite it using the metadata from this file. 5 | type: software 6 | authors: 7 | - family-names: Langa 8 | given-names: Łukasz 9 | - name: "contributors to Black" 10 | repository-code: "https://github.com/psf/black" 11 | url: "https://black.readthedocs.io/en/stable/" 12 | abstract: >- 13 | Black is the uncompromising Python code formatter. By using it, you agree to cede 14 | control over minutiae of hand-formatting. In return, Black gives you speed, 15 | determinism, and freedom from pycodestyle nagging about formatting. You will save time 16 | and mental energy for more important matters. 17 | 18 | Blackened code looks the same regardless of the project you're reading. Formatting 19 | becomes transparent after a while and you can focus on the content instead. 20 | 21 | Black makes code review faster by producing the smallest diffs possible. 22 | license: MIT 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to _Black_ 2 | 3 | Welcome future contributor! We're happy to see you willing to make the project better. 4 | 5 | If you aren't familiar with _Black_, or are looking for documentation on something 6 | specific, the [user documentation](https://black.readthedocs.io/en/latest/) is the best 7 | place to look. 8 | 9 | For getting started on contributing, please read the 10 | [contributing documentation](https://black.readthedocs.org/en/latest/contributing/) for 11 | all you need to know. 12 | 13 | Thank you, and we look forward to your contributions! 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-slim AS builder 2 | 3 | RUN mkdir /src 4 | COPY . /src/ 5 | ENV VIRTUAL_ENV=/opt/venv 6 | ENV HATCH_BUILD_HOOKS_ENABLE=1 7 | # Install build tools to compile black + dependencies 8 | RUN apt update && apt install -y build-essential git python3-dev 9 | RUN python -m venv $VIRTUAL_ENV 10 | RUN python -m pip install --no-cache-dir hatch hatch-fancy-pypi-readme hatch-vcs 11 | RUN . /opt/venv/bin/activate && pip install --no-cache-dir --upgrade pip setuptools \ 12 | && cd /src && hatch build -t wheel \ 13 | && pip install --no-cache-dir dist/*-cp* \ 14 | && pip install black[colorama,d,uvloop] 15 | 16 | FROM python:3.12-slim 17 | 18 | # copy only Python packages to limit the image size 19 | COPY --from=builder /opt/venv /opt/venv 20 | ENV PATH="/opt/venv/bin:$PATH" 21 | 22 | CMD ["/opt/venv/bin/black"] 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Łukasz Langa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the latest non-prerelease version is supported. 6 | 7 | ## Security contact information 8 | 9 | To report a security vulnerability, please use the 10 | [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the 11 | fix and disclosure. 12 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = black 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_static/license.svg: -------------------------------------------------------------------------------- 1 | licenselicenseMITMIT 2 | -------------------------------------------------------------------------------- /docs/_static/logo2-readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/docs/_static/logo2-readme.png -------------------------------------------------------------------------------- /docs/_static/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/docs/_static/logo2.png -------------------------------------------------------------------------------- /docs/_static/pypi_template.svg: -------------------------------------------------------------------------------- 1 | pypipypi$version$version 2 | -------------------------------------------------------------------------------- /docs/authors.md: -------------------------------------------------------------------------------- 1 | ```{include} ../AUTHORS.md 2 | 3 | ``` 4 | -------------------------------------------------------------------------------- /docs/change_log.md: -------------------------------------------------------------------------------- 1 | ```{include} ../CHANGES.md 2 | 3 | ``` 4 | -------------------------------------------------------------------------------- /docs/compatible_configs/flake8/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | extend-ignore = E203,E701 4 | -------------------------------------------------------------------------------- /docs/compatible_configs/flake8/setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | extend-ignore = E203,E701 4 | -------------------------------------------------------------------------------- /docs/compatible_configs/flake8/tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | extend-ignore = E203,E701 4 | -------------------------------------------------------------------------------- /docs/compatible_configs/isort/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.py] 2 | profile = black 3 | -------------------------------------------------------------------------------- /docs/compatible_configs/isort/.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | profile = black 3 | -------------------------------------------------------------------------------- /docs/compatible_configs/isort/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.isort] 2 | profile = 'black' 3 | -------------------------------------------------------------------------------- /docs/compatible_configs/isort/setup.cfg: -------------------------------------------------------------------------------- 1 | [isort] 2 | profile = black 3 | -------------------------------------------------------------------------------- /docs/compatible_configs/pycodestyle/.flake8: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | max-line-length = 88 3 | ignore = E203,E701 4 | -------------------------------------------------------------------------------- /docs/compatible_configs/pycodestyle/setup.cfg: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | max-line-length = 88 3 | ignore = E203,E701 4 | -------------------------------------------------------------------------------- /docs/compatible_configs/pycodestyle/tox.ini: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | max-line-length = 88 3 | ignore = E203,E701 4 | -------------------------------------------------------------------------------- /docs/compatible_configs/pylint/pylintrc: -------------------------------------------------------------------------------- 1 | [format] 2 | max-line-length = 88 3 | -------------------------------------------------------------------------------- /docs/compatible_configs/pylint/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pylint.format] 2 | max-line-length = "88" 3 | -------------------------------------------------------------------------------- /docs/compatible_configs/pylint/setup.cfg: -------------------------------------------------------------------------------- 1 | [pylint] 2 | max-line-length = 88 3 | -------------------------------------------------------------------------------- /docs/contributing/index.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ```{toctree} 4 | --- 5 | hidden: 6 | --- 7 | 8 | the_basics 9 | gauging_changes 10 | issue_triage 11 | release_process 12 | ``` 13 | 14 | Welcome! Happy to see you willing to make the project better. Have you read the entire 15 | [user documentation](https://black.readthedocs.io/en/latest/) yet? 16 | 17 | ```{rubric} Bird's eye view 18 | 19 | ``` 20 | 21 | In terms of inspiration, _Black_ is about as configurable as _gofmt_ (which is to say, 22 | not very). This is deliberate. _Black_ aims to provide a consistent style and take away 23 | opportunities for arguing about style. 24 | 25 | Bug reports and fixes are always welcome! Please follow the 26 | [issue templates on GitHub](https://github.com/psf/black/issues/new/choose) for best 27 | results. 28 | 29 | Before you suggest a new feature or configuration knob, ask yourself why you want it. If 30 | it enables better integration with some workflow, fixes an inconsistency, speeds things 31 | up, and so on - go for it! On the other hand, if your answer is "because I don't like a 32 | particular formatting" then you're not ready to embrace _Black_ yet. Such changes are 33 | unlikely to get accepted. You can still try but prepare to be disappointed. 34 | 35 | ```{rubric} Contents 36 | 37 | ``` 38 | 39 | This section covers the following topics: 40 | 41 | - {doc}`the_basics` 42 | - {doc}`gauging_changes` 43 | - {doc}`release_process` 44 | 45 | For an overview on contributing to the _Black_, please checkout {doc}`the_basics`. 46 | -------------------------------------------------------------------------------- /docs/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | New to _Black_? Don't worry, you've found the perfect place to get started! 4 | 5 | ## Do you like the _Black_ code style? 6 | 7 | Before using _Black_ on some of your code, it might be a good idea to first understand 8 | how _Black_ will format your code. _Black_ isn't for everyone and you may find something 9 | that is a dealbreaker for you personally, which is okay! The current _Black_ code style 10 | [is described here](./the_black_code_style/current_style.md). 11 | 12 | ## Try it out online 13 | 14 | Also, you can try out _Black_ online for minimal fuss on the 15 | [Black Playground](https://black.vercel.app) generously created by José Padilla. 16 | 17 | ## Installation 18 | 19 | _Black_ can be installed by running `pip install black`. It requires Python 3.9+ to run. 20 | If you want to format Jupyter Notebooks, install with `pip install "black[jupyter]"`. 21 | 22 | If you use pipx, you can install Black with `pipx install black`. 23 | 24 | If you can't wait for the latest _hotness_ and want to install from GitHub, use: 25 | 26 | `pip install git+https://github.com/psf/black` 27 | 28 | ## Basic usage 29 | 30 | To get started right away with sensible defaults: 31 | 32 | ```sh 33 | black {source_file_or_directory}... 34 | ``` 35 | 36 | You can run _Black_ as a package if running it as a script doesn't work: 37 | 38 | ```sh 39 | python -m black {source_file_or_directory}... 40 | ``` 41 | 42 | ## Next steps 43 | 44 | Took a look at [the _Black_ code style](./the_black_code_style/current_style.md) and 45 | tried out _Black_? Fantastic, you're ready for more. Why not explore some more on using 46 | _Black_ by reading 47 | [Usage and Configuration: The basics](./usage_and_configuration/the_basics.md). 48 | Alternatively, you can check out the 49 | [Introducing _Black_ to your project](./guides/introducing_black_to_your_project.md) 50 | guide. 51 | -------------------------------------------------------------------------------- /docs/guides/index.md: -------------------------------------------------------------------------------- 1 | # Guides 2 | 3 | ```{toctree} 4 | --- 5 | hidden: 6 | --- 7 | 8 | introducing_black_to_your_project 9 | using_black_with_other_tools 10 | ``` 11 | 12 | Wondering how to do something specific? You've found the right place! Listed below are 13 | topic specific guides available: 14 | 15 | - {doc}`introducing_black_to_your_project` 16 | - {doc}`using_black_with_other_tools` 17 | -------------------------------------------------------------------------------- /docs/integrations/index.md: -------------------------------------------------------------------------------- 1 | # Integrations 2 | 3 | ```{toctree} 4 | --- 5 | hidden: 6 | --- 7 | 8 | editors 9 | github_actions 10 | source_version_control 11 | ``` 12 | 13 | _Black_ can be integrated into many environments, providing a better and smoother 14 | experience. Documentation for integrating _Black_ with a tool can be found for the 15 | following areas: 16 | 17 | - {doc}`Editor / IDE <./editors>` 18 | - {doc}`GitHub Actions <./github_actions>` 19 | - {doc}`Source version control <./source_version_control>` 20 | 21 | Editors and tools not listed will require external contributions. 22 | 23 | Patches welcome! ✨ 🍰 ✨ 24 | 25 | Any tool can pipe code through _Black_ using its stdio mode (just 26 | [use `-` as the file name](https://www.tldp.org/LDP/abs/html/special-chars.html#DASHREF2)). 27 | The formatted code will be returned on stdout (unless `--check` was passed). _Black_ 28 | will still emit messages on stderr but that shouldn't affect your use case. 29 | 30 | This can be used for example with PyCharm's or IntelliJ's 31 | [File Watchers](https://www.jetbrains.com/help/pycharm/file-watchers.html). 32 | -------------------------------------------------------------------------------- /docs/integrations/source_version_control.md: -------------------------------------------------------------------------------- 1 | # Version control integration 2 | 3 | Use [pre-commit](https://pre-commit.com/). Once you 4 | [have it installed](https://pre-commit.com/#install), add this to the 5 | `.pre-commit-config.yaml` in your repository: 6 | 7 | ```yaml 8 | repos: 9 | # Using this mirror lets us use mypyc-compiled black, which is about 2x faster 10 | - repo: https://github.com/psf/black-pre-commit-mirror 11 | rev: 25.1.0 12 | hooks: 13 | - id: black 14 | # It is recommended to specify the latest version of Python 15 | # supported by your project here, or alternatively use 16 | # pre-commit's default_language_version, see 17 | # https://pre-commit.com/#top_level-default_language_version 18 | language_version: python3.11 19 | ``` 20 | 21 | Feel free to switch out the `rev` value to a different version of Black. 22 | 23 | Note if you'd like to use a specific commit in `rev`, you'll need to swap the repo 24 | specified from the mirror to https://github.com/psf/black. We discourage the use of 25 | branches or other mutable refs since the hook [won't auto update as you may 26 | expect][pre-commit-mutable-rev]. 27 | 28 | ## Jupyter Notebooks 29 | 30 | There is an alternate hook `black-jupyter` that expands the targets of `black` to 31 | include Jupyter Notebooks. To use this hook, simply replace the hook's `id: black` with 32 | `id: black-jupyter` in the `.pre-commit-config.yaml`: 33 | 34 | ```yaml 35 | repos: 36 | # Using this mirror lets us use mypyc-compiled black, which is about 2x faster 37 | - repo: https://github.com/psf/black-pre-commit-mirror 38 | rev: 25.1.0 39 | hooks: 40 | - id: black-jupyter 41 | # It is recommended to specify the latest version of Python 42 | # supported by your project here, or alternatively use 43 | # pre-commit's default_language_version, see 44 | # https://pre-commit.com/#top_level-default_language_version 45 | language_version: python3.11 46 | ``` 47 | 48 | ```{note} 49 | The `black-jupyter` hook became available in version 21.8b0. 50 | ``` 51 | 52 | [pre-commit-mutable-rev]: 53 | https://pre-commit.com/#using-the-latest-version-for-a-repository 54 | -------------------------------------------------------------------------------- /docs/license.md: -------------------------------------------------------------------------------- 1 | --- 2 | orphan: true 3 | --- 4 | 5 | # License 6 | 7 | ```{include} ../LICENSE 8 | 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=black 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # Used by ReadTheDocs; pinned requirements for stability. 2 | 3 | myst-parser==4.0.1 4 | Sphinx==8.2.3 5 | # Older versions break Sphinx even though they're declared to be supported. 6 | docutils==0.21.2 7 | sphinxcontrib-programoutput==0.18 8 | sphinx_copybutton==0.5.2 9 | furo==2024.8.6 10 | -------------------------------------------------------------------------------- /docs/usage_and_configuration/black_docker_image.md: -------------------------------------------------------------------------------- 1 | # Black Docker image 2 | 3 | Official _Black_ Docker images are available on 4 | [Docker Hub](https://hub.docker.com/r/pyfound/black). 5 | 6 | _Black_ images with the following tags are available: 7 | 8 | - release numbers, e.g. `21.5b2`, `21.6b0`, `21.7b0` etc.\ 9 | ℹ Recommended for users who want to use a particular version of _Black_. 10 | - `latest_release` - tag created when a new version of _Black_ is released.\ 11 | ℹ Recommended for users who want to use released versions of _Black_. It maps to 12 | [the latest release](https://github.com/psf/black/releases/latest) of _Black_. 13 | - `latest_prerelease` - tag created when a new alpha (prerelease) version of _Black_ is 14 | released.\ 15 | ℹ Recommended for users who want to preview or test alpha versions of _Black_. Note 16 | that the most recent release may be newer than any prerelease, because no prereleases 17 | are created before most releases. 18 | - `latest` - tag used for the newest image of _Black_.\ 19 | ℹ Recommended for users who always want to use the latest version of _Black_, even 20 | before it is released. 21 | 22 | There is one more tag used for _Black_ Docker images - `latest_non_release`. It is 23 | created for all unreleased 24 | [commits on the `main` branch](https://github.com/psf/black/commits/main). This tag is 25 | not meant to be used by external users. 26 | 27 | From version 23.11.0 the Docker image installs a compiled black into the image. 28 | 29 | ## Usage 30 | 31 | A permanent container doesn't have to be created to use _Black_ as a Docker image. It's 32 | enough to run _Black_ commands for the chosen image denoted as `:tag`. In the below 33 | examples, the `latest_release` tag is used. If `:tag` is omitted, the `latest` tag will 34 | be used. 35 | 36 | More about _Black_ usage can be found in 37 | [Usage and Configuration: The basics](./the_basics.md). 38 | 39 | ### Check Black version 40 | 41 | ```console 42 | $ docker run --rm pyfound/black:latest_release black --version 43 | ``` 44 | 45 | ### Check code 46 | 47 | ```console 48 | $ docker run --rm --volume $(pwd):/src --workdir /src pyfound/black:latest_release black --check . 49 | ``` 50 | 51 | _Remark_: besides [regular _Black_ exit codes](./the_basics.md) returned by `--check` 52 | option, [Docker exit codes](https://docs.docker.com/engine/reference/run/#exit-status) 53 | should also be considered. 54 | -------------------------------------------------------------------------------- /docs/usage_and_configuration/file_collection_and_discovery.md: -------------------------------------------------------------------------------- 1 | # File collection and discovery 2 | 3 | You can directly pass _Black_ files, but you can also pass directories and _Black_ will 4 | walk them, collecting files to format. It determines what files to format or skip 5 | automatically using the inclusion and exclusion regexes and as well their modification 6 | time. 7 | 8 | ## Ignoring unmodified files 9 | 10 | _Black_ remembers files it has already formatted, unless the `--diff` flag is used or 11 | code is passed via standard input. This information is stored per-user. The exact 12 | location of the file depends on the _Black_ version and the system on which _Black_ is 13 | run. The file is non-portable. The standard location on common operating systems is: 14 | 15 | - Windows: 16 | `C:\\Users\\AppData\Local\black\black\Cache\\cache...pickle` 17 | - macOS: 18 | `/Users//Library/Caches/black//cache...pickle` 19 | - Linux: 20 | `/home//.cache/black//cache...pickle` 21 | 22 | `file-mode` is an int flag that determines whether the file was formatted as 3.6+ only, 23 | as .pyi, and whether string normalization was omitted. 24 | 25 | To override the location of these files on all systems, set the environment variable 26 | `BLACK_CACHE_DIR` to the preferred location. Alternatively on macOS and Linux, set 27 | `XDG_CACHE_HOME` to your preferred location. For example, if you want to put the cache 28 | in the directory you're running _Black_ from, set `BLACK_CACHE_DIR=.cache/black`. 29 | _Black_ will then write the above files to `.cache/black`. Note that `BLACK_CACHE_DIR` 30 | will take precedence over `XDG_CACHE_HOME` if both are set. 31 | 32 | ## .gitignore 33 | 34 | If `--exclude` is not set, _Black_ will automatically ignore files and directories in 35 | `.gitignore` file(s), if present. 36 | 37 | If you want _Black_ to continue using `.gitignore` while also configuring the exclusion 38 | rules, please use `--extend-exclude`. 39 | -------------------------------------------------------------------------------- /docs/usage_and_configuration/index.md: -------------------------------------------------------------------------------- 1 | # Usage and Configuration 2 | 3 | ```{toctree} 4 | --- 5 | hidden: 6 | --- 7 | 8 | the_basics 9 | file_collection_and_discovery 10 | black_as_a_server 11 | black_docker_image 12 | ``` 13 | 14 | Sometimes, running _Black_ with its defaults and passing filepaths to it just won't cut 15 | it. Passing each file using paths will become burdensome, and maybe you would like 16 | _Black_ to not touch your files and just output diffs. And yes, you _can_ tweak certain 17 | parts of _Black_'s style, but please know that configurability in this area is 18 | purposefully limited. 19 | 20 | Using many of these more advanced features of _Black_ will require some configuration. 21 | Configuration that will either live on the command line or in a TOML configuration file. 22 | 23 | This section covers features of _Black_ and configuring _Black_ in detail: 24 | 25 | - {doc}`The basics <./the_basics>` 26 | - {doc}`File collection and discovery ` 27 | - {doc}`Black as a server (blackd) <./black_as_a_server>` 28 | - {doc}`Black Docker image <./black_docker_image>` 29 | -------------------------------------------------------------------------------- /gallery/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.2-slim 2 | 3 | # note: a single RUN to avoid too many image layers being produced 4 | RUN apt-get update \ 5 | && apt-get upgrade -y \ 6 | && apt-get install git apt-utils -y \ 7 | && git config --global user.email "black@psf.github.com" \ 8 | && git config --global user.name "Gallery/Black" 9 | 10 | COPY gallery.py / 11 | ENTRYPOINT ["python", "/gallery.py"] 12 | -------------------------------------------------------------------------------- /gallery/README.md: -------------------------------------------------------------------------------- 1 | # Gallery 2 | 3 | Gallery is a script that automates the process of applying different _Black_ versions to 4 | a selected PyPI package and seeing the results between _Black_ versions. 5 | 6 | ## Build 7 | 8 | ```console 9 | $ docker build -t black_gallery . 10 | ``` 11 | 12 | ## Run 13 | 14 | ```console 15 | $ docker run -it -v /host/output:/output -v /host/input:/input black_gallery:latest [args] 16 | ``` 17 | 18 | ``` 19 | usage: gallery.py [-h] (-p PYPI_PACKAGE | -t TOP_PACKAGES) [-b BLACK_REPO] [-v VERSION] [-w WORKERS] [-i INPUT] [-o OUTPUT] 20 | [versions [versions ...]] 21 | 22 | Black Gallery is a script that automates the process of applying different Black versions to a selected PyPI package and 23 | seeing the results between versions. 24 | 25 | positional arguments: 26 | versions 27 | 28 | optional arguments: 29 | -h, --help show this help message and exit 30 | -p PYPI_PACKAGE, --pypi-package PYPI_PACKAGE 31 | PyPI package to download. 32 | -t TOP_PACKAGES, --top-packages TOP_PACKAGES 33 | Top n PyPI packages to download. 34 | -b BLACK_REPO, --black-repo BLACK_REPO 35 | Black's Git repository. 36 | -v VERSION, --version VERSION 37 | Version for given PyPI package. Will be discarded if used with -t option. 38 | -w WORKERS, --workers WORKERS 39 | Maximum number of threads to download with at the same time. Will be discarded if used with -p 40 | option. 41 | -i INPUT, --input INPUT 42 | Input directory to read configuration. 43 | -o OUTPUT, --output OUTPUT 44 | Output directory to download and put result artifacts. 45 | ``` 46 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/scripts/__init__.py -------------------------------------------------------------------------------- /scripts/check_pre_commit_rev_in_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Check that the rev value in the example pre-commit configuration matches 3 | the latest version of Black. This saves us from forgetting to update that 4 | during the release process. 5 | 6 | Why can't we just use `rev: stable` and call it a day? Well pre-commit 7 | won't auto update the hook as you may expect (and for good reasons, some 8 | technical and some pragmatic). Encouraging bad practice is also just 9 | not ideal. xref: https://github.com/psf/black/issues/420 10 | """ 11 | 12 | import os 13 | import sys 14 | 15 | import commonmark 16 | import yaml 17 | from bs4 import BeautifulSoup # type: ignore[import-untyped] 18 | 19 | 20 | def main(changes: str, source_version_control: str) -> None: 21 | changes_html = commonmark.commonmark(changes) 22 | changes_soup = BeautifulSoup(changes_html, "html.parser") 23 | headers = changes_soup.find_all("h2") 24 | latest_tag, *_ = [ 25 | header.string for header in headers if header.string != "Unreleased" 26 | ] 27 | 28 | source_version_control_html = commonmark.commonmark(source_version_control) 29 | source_version_control_soup = BeautifulSoup( 30 | source_version_control_html, "html.parser" 31 | ) 32 | pre_commit_repos = yaml.safe_load( 33 | source_version_control_soup.find(class_="language-yaml").string 34 | )["repos"] 35 | 36 | for repo in pre_commit_repos: 37 | pre_commit_rev = repo["rev"] 38 | if not pre_commit_rev == latest_tag: 39 | print( 40 | "Please set the rev in ``source_version_control.md`` to be the latest " 41 | f"one.\nExpected {latest_tag}, got {pre_commit_rev}.\n" 42 | ) 43 | sys.exit(1) 44 | 45 | 46 | if __name__ == "__main__": 47 | with open("CHANGES.md", encoding="utf-8") as fd: 48 | changes = fd.read() 49 | with open( 50 | os.path.join("docs", "integrations", "source_version_control.md"), 51 | encoding="utf-8", 52 | ) as fd: 53 | source_version_control = fd.read() 54 | main(changes, source_version_control) 55 | -------------------------------------------------------------------------------- /scripts/check_version_in_basics_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Check that the rev value in the example from ``the_basics.md`` matches 3 | the latest version of Black. This saves us from forgetting to update that 4 | during the release process. 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | import commonmark 11 | from bs4 import BeautifulSoup # type: ignore[import-untyped] 12 | 13 | 14 | def main(changes: str, the_basics: str) -> None: 15 | changes_html = commonmark.commonmark(changes) 16 | changes_soup = BeautifulSoup(changes_html, "html.parser") 17 | headers = changes_soup.find_all("h2") 18 | tags = [header.string for header in headers if header.string != "Unreleased"] 19 | latest_tag = tags[0] 20 | 21 | the_basics_html = commonmark.commonmark(the_basics) 22 | the_basics_soup = BeautifulSoup(the_basics_html, "html.parser") 23 | version_examples = [ 24 | code_block.string 25 | for code_block in the_basics_soup.find_all(class_="language-console") 26 | if "$ black --version" in code_block.string 27 | ] 28 | 29 | for tag in tags: 30 | for version_example in version_examples: 31 | if tag in version_example and tag != latest_tag: 32 | print( 33 | "Please set the version in the ``black --version`` " 34 | "examples from ``the_basics.md`` to be the latest one.\n" 35 | f"Expected {latest_tag}, got {tag}.\n" 36 | ) 37 | sys.exit(1) 38 | 39 | 40 | if __name__ == "__main__": 41 | with open("CHANGES.md", encoding="utf-8") as fd: 42 | changes = fd.read() 43 | with open( 44 | os.path.join("docs", "usage_and_configuration", "the_basics.md"), 45 | encoding="utf-8", 46 | ) as fd: 47 | the_basics = fd.read() 48 | main(changes, the_basics) 49 | -------------------------------------------------------------------------------- /src/black/__main__.py: -------------------------------------------------------------------------------- 1 | from black import patched_main 2 | 3 | patched_main() 4 | -------------------------------------------------------------------------------- /src/black/const.py: -------------------------------------------------------------------------------- 1 | DEFAULT_LINE_LENGTH = 88 2 | DEFAULT_EXCLUDES = r"/(\.direnv|\.eggs|\.git|\.hg|\.ipynb_checkpoints|\.mypy_cache|\.nox|\.pytest_cache|\.ruff_cache|\.tox|\.svn|\.venv|\.vscode|__pypackages__|_build|buck-out|build|dist|venv)/" # noqa: B950 3 | DEFAULT_INCLUDES = r"(\.pyi?|\.ipynb)$" 4 | STDIN_PLACEHOLDER = "__BLACK_STDIN_FILENAME__" 5 | -------------------------------------------------------------------------------- /src/black/debug.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterator 2 | from dataclasses import dataclass, field 3 | from typing import Any, TypeVar, Union 4 | 5 | from black.nodes import Visitor 6 | from black.output import out 7 | from black.parsing import lib2to3_parse 8 | from blib2to3.pgen2 import token 9 | from blib2to3.pytree import Leaf, Node, type_repr 10 | 11 | LN = Union[Leaf, Node] 12 | T = TypeVar("T") 13 | 14 | 15 | @dataclass 16 | class DebugVisitor(Visitor[T]): 17 | tree_depth: int = 0 18 | list_output: list[str] = field(default_factory=list) 19 | print_output: bool = True 20 | 21 | def out(self, message: str, *args: Any, **kwargs: Any) -> None: 22 | self.list_output.append(message) 23 | if self.print_output: 24 | out(message, *args, **kwargs) 25 | 26 | def visit_default(self, node: LN) -> Iterator[T]: 27 | indent = " " * (2 * self.tree_depth) 28 | if isinstance(node, Node): 29 | _type = type_repr(node.type) 30 | self.out(f"{indent}{_type}", fg="yellow") 31 | self.tree_depth += 1 32 | for child in node.children: 33 | yield from self.visit(child) 34 | 35 | self.tree_depth -= 1 36 | self.out(f"{indent}/{_type}", fg="yellow", bold=False) 37 | else: 38 | _type = token.tok_name.get(node.type, str(node.type)) 39 | self.out(f"{indent}{_type}", fg="blue", nl=False) 40 | if node.prefix: 41 | # We don't have to handle prefixes for `Node` objects since 42 | # that delegates to the first child anyway. 43 | self.out(f" {node.prefix!r}", fg="green", bold=False, nl=False) 44 | self.out(f" {node.value!r}", fg="blue", bold=False) 45 | 46 | @classmethod 47 | def show(cls, code: Union[str, Leaf, Node]) -> None: 48 | """Pretty-print the lib2to3 AST of a given string of `code`. 49 | 50 | Convenience method for debugging. 51 | """ 52 | v: DebugVisitor[None] = DebugVisitor() 53 | if isinstance(code, str): 54 | code = lib2to3_parse(code) 55 | list(v.visit(code)) 56 | -------------------------------------------------------------------------------- /src/black/numerics.py: -------------------------------------------------------------------------------- 1 | """ 2 | Formatting numeric literals. 3 | """ 4 | 5 | from blib2to3.pytree import Leaf 6 | 7 | 8 | def format_hex(text: str) -> str: 9 | """ 10 | Formats a hexadecimal string like "0x12B3" 11 | """ 12 | before, after = text[:2], text[2:] 13 | return f"{before}{after.upper()}" 14 | 15 | 16 | def format_scientific_notation(text: str) -> str: 17 | """Formats a numeric string utilizing scientific notation""" 18 | before, after = text.split("e") 19 | sign = "" 20 | if after.startswith("-"): 21 | after = after[1:] 22 | sign = "-" 23 | elif after.startswith("+"): 24 | after = after[1:] 25 | before = format_float_or_int_string(before) 26 | return f"{before}e{sign}{after}" 27 | 28 | 29 | def format_complex_number(text: str) -> str: 30 | """Formats a complex string like `10j`""" 31 | number = text[:-1] 32 | suffix = text[-1] 33 | return f"{format_float_or_int_string(number)}{suffix}" 34 | 35 | 36 | def format_float_or_int_string(text: str) -> str: 37 | """Formats a float string like "1.0".""" 38 | if "." not in text: 39 | return text 40 | 41 | before, after = text.split(".") 42 | return f"{before or 0}.{after or 0}" 43 | 44 | 45 | def normalize_numeric_literal(leaf: Leaf) -> None: 46 | """Normalizes numeric (float, int, and complex) literals. 47 | 48 | All letters used in the representation are normalized to lowercase.""" 49 | text = leaf.value.lower() 50 | if text.startswith(("0o", "0b")): 51 | # Leave octal and binary literals alone. 52 | pass 53 | elif text.startswith("0x"): 54 | text = format_hex(text) 55 | elif "e" in text: 56 | text = format_scientific_notation(text) 57 | elif text.endswith("j"): 58 | text = format_complex_number(text) 59 | else: 60 | text = format_float_or_int_string(text) 61 | leaf.value = text 62 | -------------------------------------------------------------------------------- /src/black/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/src/black/py.typed -------------------------------------------------------------------------------- /src/black/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/src/black/resources/__init__.py -------------------------------------------------------------------------------- /src/black/rusty.py: -------------------------------------------------------------------------------- 1 | """An error-handling model influenced by that used by the Rust programming language 2 | 3 | See https://doc.rust-lang.org/book/ch09-00-error-handling.html. 4 | """ 5 | 6 | from typing import Generic, TypeVar, Union 7 | 8 | T = TypeVar("T") 9 | E = TypeVar("E", bound=Exception) 10 | 11 | 12 | class Ok(Generic[T]): 13 | def __init__(self, value: T) -> None: 14 | self._value = value 15 | 16 | def ok(self) -> T: 17 | return self._value 18 | 19 | 20 | class Err(Generic[E]): 21 | def __init__(self, e: E) -> None: 22 | self._e = e 23 | 24 | def err(self) -> E: 25 | return self._e 26 | 27 | 28 | Result = Union[Ok[T], Err[E]] 29 | -------------------------------------------------------------------------------- /src/black/schema.py: -------------------------------------------------------------------------------- 1 | import importlib.resources 2 | import json 3 | from typing import Any 4 | 5 | 6 | def get_schema(tool_name: str = "black") -> Any: 7 | """Get the stored complete schema for black's settings.""" 8 | assert tool_name == "black", "Only black is supported." 9 | 10 | pkg = "black.resources" 11 | fname = "black.schema.json" 12 | 13 | schema = importlib.resources.files(pkg).joinpath(fname) 14 | with schema.open(encoding="utf-8") as f: 15 | return json.load(f) 16 | -------------------------------------------------------------------------------- /src/blackd/__main__.py: -------------------------------------------------------------------------------- 1 | import blackd 2 | 3 | blackd.patched_main() 4 | -------------------------------------------------------------------------------- /src/blackd/middlewares.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Awaitable, Callable, Iterable 2 | 3 | from aiohttp.typedefs import Middleware 4 | from aiohttp.web_middlewares import middleware 5 | from aiohttp.web_request import Request 6 | from aiohttp.web_response import StreamResponse 7 | 8 | Handler = Callable[[Request], Awaitable[StreamResponse]] 9 | 10 | 11 | def cors(allow_headers: Iterable[str]) -> Middleware: 12 | @middleware 13 | async def impl(request: Request, handler: Handler) -> StreamResponse: 14 | is_options = request.method == "OPTIONS" 15 | is_preflight = is_options and "Access-Control-Request-Method" in request.headers 16 | if is_preflight: 17 | resp = StreamResponse() 18 | else: 19 | resp = await handler(request) 20 | 21 | origin = request.headers.get("Origin") 22 | if not origin: 23 | return resp 24 | 25 | resp.headers["Access-Control-Allow-Origin"] = "*" 26 | resp.headers["Access-Control-Expose-Headers"] = "*" 27 | if is_options: 28 | resp.headers["Access-Control-Allow-Headers"] = ", ".join(allow_headers) 29 | resp.headers["Access-Control-Allow-Methods"] = ", ".join( 30 | ("OPTIONS", "POST") 31 | ) 32 | 33 | return resp 34 | 35 | return impl 36 | -------------------------------------------------------------------------------- /src/blib2to3/PatternGrammar.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2006 Google, Inc. All Rights Reserved. 2 | # Licensed to PSF under a Contributor Agreement. 3 | 4 | # A grammar to describe tree matching patterns. 5 | # Not shown here: 6 | # - 'TOKEN' stands for any token (leaf node) 7 | # - 'any' stands for any node (leaf or interior) 8 | # With 'any' we can still specify the sub-structure. 9 | 10 | # The start symbol is 'Matcher'. 11 | 12 | Matcher: Alternatives ENDMARKER 13 | 14 | Alternatives: Alternative ('|' Alternative)* 15 | 16 | Alternative: (Unit | NegatedUnit)+ 17 | 18 | Unit: [NAME '='] ( STRING [Repeater] 19 | | NAME [Details] [Repeater] 20 | | '(' Alternatives ')' [Repeater] 21 | | '[' Alternatives ']' 22 | ) 23 | 24 | NegatedUnit: 'not' (STRING | NAME [Details] | '(' Alternatives ')') 25 | 26 | Repeater: '*' | '+' | '{' NUMBER [',' NUMBER] '}' 27 | 28 | Details: '<' Alternatives '>' 29 | -------------------------------------------------------------------------------- /src/blib2to3/README: -------------------------------------------------------------------------------- 1 | A subset of lib2to3 taken from Python 3.7.0b2. Commit hash: 2 | 9c17e3a1987004b8bcfbe423953aad84493a7984 3 | 4 | Reasons for forking: 5 | 6 | - consistent handling of f-strings for users of Python < 3.6.2 7 | - backport of BPO-33064 that fixes parsing files with trailing commas after \*args and 8 | \*\*kwargs 9 | - backport of GH-6143 that restores the ability to reformat legacy usage of `async` 10 | - support all types of string literals 11 | - better ability to debug (better reprs) 12 | - INDENT and DEDENT don't hold whitespace and comment prefixes 13 | - ability to Cythonize 14 | 15 | Change Log: 16 | 17 | - Changes default logger used by Driver 18 | - Backported the following upstream parser changes: 19 | - "bpo-42381: Allow walrus in set literals and set comprehensions (GH-23332)" 20 | https://github.com/python/cpython/commit/cae60187cf7a7b26281d012e1952fafe4e2e97e9 21 | - "bpo-42316: Allow unparenthesized walrus operator in indexes (GH-23317)" 22 | https://github.com/python/cpython/commit/b0aba1fcdc3da952698d99aec2334faa79a8b68c 23 | - Tweaks to help mypyc compile faster code (including inlining type information, 24 | "Final-ing", etc.) 25 | -------------------------------------------------------------------------------- /src/blib2to3/__init__.py: -------------------------------------------------------------------------------- 1 | # empty 2 | -------------------------------------------------------------------------------- /src/blib2to3/pgen2/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. 2 | # Licensed to PSF under a Contributor Agreement. 3 | 4 | """The pgen2 package.""" 5 | -------------------------------------------------------------------------------- /src/blib2to3/pgen2/literals.py: -------------------------------------------------------------------------------- 1 | # Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved. 2 | # Licensed to PSF under a Contributor Agreement. 3 | 4 | """Safely evaluate Python string literals without using eval().""" 5 | 6 | import re 7 | 8 | simple_escapes: dict[str, str] = { 9 | "a": "\a", 10 | "b": "\b", 11 | "f": "\f", 12 | "n": "\n", 13 | "r": "\r", 14 | "t": "\t", 15 | "v": "\v", 16 | "'": "'", 17 | '"': '"', 18 | "\\": "\\", 19 | } 20 | 21 | 22 | def escape(m: re.Match[str]) -> str: 23 | all, tail = m.group(0, 1) 24 | assert all.startswith("\\") 25 | esc = simple_escapes.get(tail) 26 | if esc is not None: 27 | return esc 28 | if tail.startswith("x"): 29 | hexes = tail[1:] 30 | if len(hexes) < 2: 31 | raise ValueError("invalid hex string escape ('\\%s')" % tail) 32 | try: 33 | i = int(hexes, 16) 34 | except ValueError: 35 | raise ValueError("invalid hex string escape ('\\%s')" % tail) from None 36 | else: 37 | try: 38 | i = int(tail, 8) 39 | except ValueError: 40 | raise ValueError("invalid octal string escape ('\\%s')" % tail) from None 41 | return chr(i) 42 | 43 | 44 | def evalString(s: str) -> str: 45 | assert s.startswith("'") or s.startswith('"'), repr(s[:1]) 46 | q = s[0] 47 | if s[:3] == q * 3: 48 | q = q * 3 49 | assert s.endswith(q), repr(s[-len(q) :]) 50 | assert len(s) >= 2 * len(q) 51 | s = s[len(q) : -len(q)] 52 | return re.sub(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3})", escape, s) 53 | 54 | 55 | def test() -> None: 56 | for i in range(256): 57 | c = chr(i) 58 | s = repr(c) 59 | e = evalString(s) 60 | if e != c: 61 | print(i, c, s, e) 62 | 63 | 64 | if __name__ == "__main__": 65 | test() 66 | -------------------------------------------------------------------------------- /src/blib2to3/pgen2/token.py: -------------------------------------------------------------------------------- 1 | """Token constants (from "token.h").""" 2 | 3 | from typing import Final 4 | 5 | # Taken from Python (r53757) and modified to include some tokens 6 | # originally monkeypatched in by pgen2.tokenize 7 | 8 | # --start constants-- 9 | ENDMARKER: Final = 0 10 | NAME: Final = 1 11 | NUMBER: Final = 2 12 | STRING: Final = 3 13 | NEWLINE: Final = 4 14 | INDENT: Final = 5 15 | DEDENT: Final = 6 16 | LPAR: Final = 7 17 | RPAR: Final = 8 18 | LSQB: Final = 9 19 | RSQB: Final = 10 20 | COLON: Final = 11 21 | COMMA: Final = 12 22 | SEMI: Final = 13 23 | PLUS: Final = 14 24 | MINUS: Final = 15 25 | STAR: Final = 16 26 | SLASH: Final = 17 27 | VBAR: Final = 18 28 | AMPER: Final = 19 29 | LESS: Final = 20 30 | GREATER: Final = 21 31 | EQUAL: Final = 22 32 | DOT: Final = 23 33 | PERCENT: Final = 24 34 | BACKQUOTE: Final = 25 35 | LBRACE: Final = 26 36 | RBRACE: Final = 27 37 | EQEQUAL: Final = 28 38 | NOTEQUAL: Final = 29 39 | LESSEQUAL: Final = 30 40 | GREATEREQUAL: Final = 31 41 | TILDE: Final = 32 42 | CIRCUMFLEX: Final = 33 43 | LEFTSHIFT: Final = 34 44 | RIGHTSHIFT: Final = 35 45 | DOUBLESTAR: Final = 36 46 | PLUSEQUAL: Final = 37 47 | MINEQUAL: Final = 38 48 | STAREQUAL: Final = 39 49 | SLASHEQUAL: Final = 40 50 | PERCENTEQUAL: Final = 41 51 | AMPEREQUAL: Final = 42 52 | VBAREQUAL: Final = 43 53 | CIRCUMFLEXEQUAL: Final = 44 54 | LEFTSHIFTEQUAL: Final = 45 55 | RIGHTSHIFTEQUAL: Final = 46 56 | DOUBLESTAREQUAL: Final = 47 57 | DOUBLESLASH: Final = 48 58 | DOUBLESLASHEQUAL: Final = 49 59 | AT: Final = 50 60 | ATEQUAL: Final = 51 61 | OP: Final = 52 62 | COMMENT: Final = 53 63 | NL: Final = 54 64 | RARROW: Final = 55 65 | AWAIT: Final = 56 66 | ASYNC: Final = 57 67 | ERRORTOKEN: Final = 58 68 | COLONEQUAL: Final = 59 69 | FSTRING_START: Final = 60 70 | FSTRING_MIDDLE: Final = 61 71 | FSTRING_END: Final = 62 72 | BANG: Final = 63 73 | N_TOKENS: Final = 64 74 | NT_OFFSET: Final = 256 75 | # --end constants-- 76 | 77 | tok_name: Final[dict[int, str]] = {} 78 | for _name, _value in list(globals().items()): 79 | if type(_value) is int: 80 | tok_name[_value] = _name 81 | 82 | 83 | def ISTERMINAL(x: int) -> bool: 84 | return x < NT_OFFSET 85 | 86 | 87 | def ISNONTERMINAL(x: int) -> bool: 88 | return x >= NT_OFFSET 89 | 90 | 91 | def ISEOF(x: int) -> bool: 92 | return x == ENDMARKER 93 | -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | coverage >= 5.3 2 | pre-commit 3 | pytest >= 6.1.1 4 | pytest-xdist >= 3.0.2 5 | pytest-cov >= 4.1.0 6 | tox 7 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | pytest_plugins = ["tests.optional"] 4 | 5 | PRINT_FULL_TREE: bool = False 6 | PRINT_TREE_DIFF: bool = True 7 | 8 | 9 | def pytest_addoption(parser: pytest.Parser) -> None: 10 | parser.addoption( 11 | "--print-full-tree", 12 | action="store_true", 13 | default=False, 14 | help="print full syntax trees on failed tests", 15 | ) 16 | parser.addoption( 17 | "--print-tree-diff", 18 | action="store_true", 19 | default=True, 20 | help="print diff of syntax trees on failed tests", 21 | ) 22 | 23 | 24 | def pytest_configure(config: pytest.Config) -> None: 25 | global PRINT_FULL_TREE 26 | global PRINT_TREE_DIFF 27 | PRINT_FULL_TREE = config.getoption("--print-full-tree") 28 | PRINT_TREE_DIFF = config.getoption("--print-tree-diff") 29 | -------------------------------------------------------------------------------- /tests/data/cases/annotations.py: -------------------------------------------------------------------------------- 1 | # regression test for #1765 2 | class Foo: 3 | def foo(self): 4 | if True: 5 | content_ids: Mapping[ 6 | str, Optional[ContentId] 7 | ] = self.publisher_content_store.store_config_contents(files) 8 | 9 | # output 10 | 11 | # regression test for #1765 12 | class Foo: 13 | def foo(self): 14 | if True: 15 | content_ids: Mapping[str, Optional[ContentId]] = ( 16 | self.publisher_content_store.store_config_contents(files) 17 | ) -------------------------------------------------------------------------------- /tests/data/cases/async_stmts.py: -------------------------------------------------------------------------------- 1 | async def func() -> (int): 2 | return 0 3 | 4 | 5 | @decorated 6 | async def func() -> (int): 7 | return 0 8 | 9 | 10 | async for (item) in async_iter: 11 | pass 12 | 13 | 14 | # output 15 | 16 | 17 | async def func() -> int: 18 | return 0 19 | 20 | 21 | @decorated 22 | async def func() -> int: 23 | return 0 24 | 25 | 26 | async for item in async_iter: 27 | pass 28 | -------------------------------------------------------------------------------- /tests/data/cases/attribute_access_on_number_literals.py: -------------------------------------------------------------------------------- 1 | x = 123456789 .bit_count() 2 | x = (123456).__abs__() 3 | x = .1.is_integer() 4 | x = 1. .imag 5 | x = 1E+1.imag 6 | x = 1E-1.real 7 | x = 123456789.123456789.hex() 8 | x = 123456789.123456789E123456789 .real 9 | x = 123456789E123456789 .conjugate() 10 | x = 123456789J.real 11 | x = 123456789.123456789J.__add__(0b1011.bit_length()) 12 | x = 0XB1ACC.conjugate() 13 | x = 0B1011 .conjugate() 14 | x = 0O777 .real 15 | x = 0.000000006 .hex() 16 | x = -100.0000J 17 | 18 | if 10 .real: 19 | ... 20 | 21 | y = 100[no] 22 | y = 100(no) 23 | 24 | # output 25 | 26 | x = (123456789).bit_count() 27 | x = (123456).__abs__() 28 | x = (0.1).is_integer() 29 | x = (1.0).imag 30 | x = (1e1).imag 31 | x = (1e-1).real 32 | x = (123456789.123456789).hex() 33 | x = (123456789.123456789e123456789).real 34 | x = (123456789e123456789).conjugate() 35 | x = 123456789j.real 36 | x = 123456789.123456789j.__add__(0b1011.bit_length()) 37 | x = 0xB1ACC.conjugate() 38 | x = 0b1011.conjugate() 39 | x = 0o777.real 40 | x = (0.000000006).hex() 41 | x = -100.0000j 42 | 43 | if (10).real: 44 | ... 45 | 46 | y = 100[no] 47 | y = 100(no) 48 | -------------------------------------------------------------------------------- /tests/data/cases/backslash_before_indent.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | class Plotter: 3 | \ 4 | pass 5 | 6 | class AnotherCase: 7 | \ 8 | """Some 9 | \ 10 | Docstring 11 | """ 12 | 13 | # output 14 | 15 | class Plotter: 16 | 17 | pass 18 | 19 | 20 | class AnotherCase: 21 | """Some 22 | \ 23 | Docstring 24 | """ 25 | -------------------------------------------------------------------------------- /tests/data/cases/beginning_backslash.py: -------------------------------------------------------------------------------- 1 | \ 2 | 3 | 4 | 5 | 6 | 7 | print("hello, world") 8 | 9 | # output 10 | 11 | 12 | print("hello, world") 13 | -------------------------------------------------------------------------------- /tests/data/cases/bracketmatch.py: -------------------------------------------------------------------------------- 1 | for ((x in {}) or {})['a'] in x: 2 | pass 3 | pem_spam = lambda l, spam = { 4 | "x": 3 5 | }: not spam.get(l.strip()) 6 | lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x 7 | 8 | 9 | # output 10 | 11 | 12 | for ((x in {}) or {})["a"] in x: 13 | pass 14 | pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip()) 15 | lambda x=lambda y={1: 3}: y["x" : lambda y: {1: 2}]: x 16 | -------------------------------------------------------------------------------- /tests/data/cases/bytes_docstring.py: -------------------------------------------------------------------------------- 1 | def bitey(): 2 | b" not a docstring" 3 | 4 | def bitey2(): 5 | b' also not a docstring' 6 | 7 | def triple_quoted_bytes(): 8 | b""" not a docstring""" 9 | 10 | def triple_quoted_bytes2(): 11 | b''' also not a docstring''' 12 | 13 | def capitalized_bytes(): 14 | B" NOT A DOCSTRING" 15 | 16 | # output 17 | def bitey(): 18 | b" not a docstring" 19 | 20 | 21 | def bitey2(): 22 | b" also not a docstring" 23 | 24 | 25 | def triple_quoted_bytes(): 26 | b""" not a docstring""" 27 | 28 | 29 | def triple_quoted_bytes2(): 30 | b""" also not a docstring""" 31 | 32 | 33 | def capitalized_bytes(): 34 | b" NOT A DOCSTRING" -------------------------------------------------------------------------------- /tests/data/cases/class_blank_parentheses.py: -------------------------------------------------------------------------------- 1 | class SimpleClassWithBlankParentheses(): 2 | pass 3 | class ClassWithSpaceParentheses ( ): 4 | first_test_data = 90 5 | second_test_data = 100 6 | def test_func(self): 7 | return None 8 | class ClassWithEmptyFunc(object): 9 | 10 | def func_with_blank_parentheses(): 11 | return 5 12 | 13 | 14 | def public_func_with_blank_parentheses(): 15 | return None 16 | def class_under_the_func_with_blank_parentheses(): 17 | class InsideFunc(): 18 | pass 19 | class NormalClass ( 20 | ): 21 | def func_for_testing(self, first, second): 22 | sum = first + second 23 | return sum 24 | 25 | 26 | # output 27 | 28 | 29 | class SimpleClassWithBlankParentheses: 30 | pass 31 | 32 | 33 | class ClassWithSpaceParentheses: 34 | first_test_data = 90 35 | second_test_data = 100 36 | 37 | def test_func(self): 38 | return None 39 | 40 | 41 | class ClassWithEmptyFunc(object): 42 | 43 | def func_with_blank_parentheses(): 44 | return 5 45 | 46 | 47 | def public_func_with_blank_parentheses(): 48 | return None 49 | 50 | 51 | def class_under_the_func_with_blank_parentheses(): 52 | class InsideFunc: 53 | pass 54 | 55 | 56 | class NormalClass: 57 | def func_for_testing(self, first, second): 58 | sum = first + second 59 | return sum 60 | -------------------------------------------------------------------------------- /tests/data/cases/comment_after_escaped_newline.py: -------------------------------------------------------------------------------- 1 | def bob(): \ 2 | # pylint: disable=W9016 3 | pass 4 | 5 | 6 | def bobtwo(): \ 7 | \ 8 | # some comment here 9 | pass 10 | 11 | # output 12 | 13 | def bob(): # pylint: disable=W9016 14 | pass 15 | 16 | 17 | def bobtwo(): 18 | 19 | # some comment here 20 | pass 21 | -------------------------------------------------------------------------------- /tests/data/cases/comment_type_hint.py: -------------------------------------------------------------------------------- 1 | # flags: --no-preview-line-length-1 2 | # split out from comments2 as it does not work with line-length=1, losing the comment 3 | a = "type comment with trailing space" # type: str 4 | -------------------------------------------------------------------------------- /tests/data/cases/comments.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # fmt: on 3 | # Some license here. 4 | # 5 | # Has many lines. Many, many lines. 6 | # Many, many, many lines. 7 | """Module docstring. 8 | 9 | Possibly also many, many lines. 10 | """ 11 | 12 | import os.path 13 | import sys 14 | 15 | import a 16 | from b.c import X # some noqa comment 17 | 18 | try: 19 | import fast 20 | except ImportError: 21 | import slow as fast 22 | 23 | 24 | # Some comment before a function. 25 | y = 1 26 | ( 27 | # some strings 28 | y # type: ignore 29 | ) 30 | 31 | 32 | def function(default=None): 33 | """Docstring comes first. 34 | 35 | Possibly many lines. 36 | """ 37 | # FIXME: Some comment about why this function is crap but still in production. 38 | import inner_imports 39 | 40 | if inner_imports.are_evil(): 41 | # Explains why we have this if. 42 | # In great detail indeed. 43 | x = X() 44 | return x.method1() # type: ignore 45 | 46 | # This return is also commented for some reason. 47 | return default 48 | 49 | 50 | # Explains why we use global state. 51 | GLOBAL_STATE = {"a": a(1), "b": a(2), "c": a(3)} 52 | 53 | 54 | # Another comment! 55 | # This time two lines. 56 | 57 | 58 | class Foo: 59 | """Docstring for class Foo. Example from Sphinx docs.""" 60 | 61 | #: Doc comment for class attribute Foo.bar. 62 | #: It can have multiple lines. 63 | bar = 1 64 | 65 | flox = 1.5 #: Doc comment for Foo.flox. One line only. 66 | 67 | baz = 2 68 | """Docstring for class attribute Foo.baz.""" 69 | 70 | def __init__(self): 71 | #: Doc comment for instance attribute qux. 72 | self.qux = 3 73 | 74 | self.spam = 4 75 | """Docstring for instance attribute spam.""" 76 | 77 | 78 | #'

This is pweave!

79 | 80 | 81 | @fast(really=True) 82 | async def wat(): 83 | # This comment, for some reason \ 84 | # contains a trailing backslash. 85 | async with X.open_async() as x: # Some more comments 86 | result = await x.method1() 87 | # Comment after ending a block. 88 | if result: 89 | print("A OK", file=sys.stdout) 90 | # Comment between things. 91 | print() 92 | 93 | 94 | # Some closing comments. 95 | # Maybe Vim or Emacs directives for formatting. 96 | # Who knows. 97 | -------------------------------------------------------------------------------- /tests/data/cases/comments3.py: -------------------------------------------------------------------------------- 1 | # The percent-percent comments are Spyder IDE cells. 2 | 3 | 4 | # %% 5 | def func(): 6 | x = """ 7 | a really long string 8 | """ 9 | lcomp3 = [ 10 | # This one is actually too long to fit in a single line. 11 | element.split("\n", 1)[0] 12 | # yup 13 | for element in collection.select_elements() 14 | # right 15 | if element is not None 16 | ] 17 | # Capture each of the exceptions in the MultiError along with each of their causes and contexts 18 | if isinstance(exc_value, MultiError): 19 | embedded = [] 20 | for exc in exc_value.exceptions: 21 | if exc not in _seen: 22 | embedded.append( 23 | # This should be left alone (before) 24 | traceback.TracebackException.from_exception( 25 | exc, 26 | limit=limit, 27 | lookup_lines=lookup_lines, 28 | capture_locals=capture_locals, 29 | # copy the set of _seen exceptions so that duplicates 30 | # shared between sub-exceptions are not omitted 31 | _seen=set(_seen), 32 | ) 33 | # This should be left alone (after) 34 | ) 35 | 36 | # everything is fine if the expression isn't nested 37 | traceback.TracebackException.from_exception( 38 | exc, 39 | limit=limit, 40 | lookup_lines=lookup_lines, 41 | capture_locals=capture_locals, 42 | # copy the set of _seen exceptions so that duplicates 43 | # shared between sub-exceptions are not omitted 44 | _seen=set(_seen), 45 | ) 46 | 47 | 48 | # %% -------------------------------------------------------------------------------- /tests/data/cases/comments5.py: -------------------------------------------------------------------------------- 1 | while True: 2 | if something.changed: 3 | do.stuff() # trailing comment 4 | # Comment belongs to the `if` block. 5 | # This one belongs to the `while` block. 6 | 7 | # Should this one, too? I guess so. 8 | 9 | # This one is properly standalone now. 10 | 11 | for i in range(100): 12 | # first we do this 13 | if i % 33 == 0: 14 | break 15 | 16 | # then we do this 17 | print(i) 18 | # and finally we loop around 19 | 20 | with open(some_temp_file) as f: 21 | data = f.read() 22 | 23 | try: 24 | with open(some_other_file) as w: 25 | w.write(data) 26 | 27 | except OSError: 28 | print("problems") 29 | 30 | import sys 31 | 32 | 33 | # leading function comment 34 | def wat(): 35 | ... 36 | # trailing function comment 37 | 38 | 39 | # SECTION COMMENT 40 | 41 | 42 | # leading 1 43 | @deco1 44 | # leading 2 45 | @deco2(with_args=True) 46 | # leading 3 47 | @deco3 48 | def decorated1(): ... 49 | 50 | 51 | # leading 1 52 | @deco1 53 | # leading 2 54 | @deco2(with_args=True) 55 | # leading function comment 56 | def decorated1(): ... 57 | 58 | 59 | # Note: this is fixed in 60 | # Preview.empty_lines_before_class_or_def_with_leading_comments. 61 | # In the current style, the user will have to split those lines by hand. 62 | some_instruction 63 | 64 | 65 | # This comment should be split from `some_instruction` by two lines but isn't. 66 | def g(): ... 67 | 68 | 69 | if __name__ == "__main__": 70 | main() 71 | -------------------------------------------------------------------------------- /tests/data/cases/comments8.py: -------------------------------------------------------------------------------- 1 | # The percent-percent comments are Spyder IDE cells. 2 | # Both `#%%`` and `# %%` are accepted, so `black` standardises 3 | # to the latter. 4 | 5 | #%% 6 | # %% 7 | 8 | # output 9 | 10 | # The percent-percent comments are Spyder IDE cells. 11 | # Both `#%%`` and `# %%` are accepted, so `black` standardises 12 | # to the latter. 13 | 14 | # %% 15 | # %% 16 | -------------------------------------------------------------------------------- /tests/data/cases/comments_in_double_parens.py: -------------------------------------------------------------------------------- 1 | if ( 2 | True 3 | # sdf 4 | ): 5 | print("hw") 6 | 7 | if (( 8 | True 9 | # sdf 10 | )): 11 | print("hw") 12 | 13 | if (( 14 | # type: ignore 15 | True 16 | )): 17 | print("hw") 18 | 19 | if (( 20 | True 21 | # type: ignore 22 | )): 23 | print("hw") 24 | 25 | if ( 26 | # a long comment about 27 | # the condition below 28 | (a or b) 29 | ): 30 | pass 31 | 32 | def return_true(): 33 | return ( 34 | ( 35 | True # this comment gets removed accidentally 36 | ) 37 | ) 38 | 39 | def return_true(): 40 | return (True) # this comment gets removed accidentally 41 | 42 | 43 | if ( 44 | # huh comment 45 | (True) 46 | ): 47 | ... 48 | 49 | if ( 50 | # huh 51 | ( 52 | # comment 53 | True 54 | ) 55 | ): 56 | ... 57 | 58 | 59 | # output 60 | 61 | if ( 62 | True 63 | # sdf 64 | ): 65 | print("hw") 66 | 67 | if ( 68 | True 69 | # sdf 70 | ): 71 | print("hw") 72 | 73 | if ( 74 | # type: ignore 75 | True 76 | ): 77 | print("hw") 78 | 79 | if ( 80 | True 81 | # type: ignore 82 | ): 83 | print("hw") 84 | 85 | if ( 86 | # a long comment about 87 | # the condition below 88 | a 89 | or b 90 | ): 91 | pass 92 | 93 | 94 | def return_true(): 95 | return True # this comment gets removed accidentally 96 | 97 | 98 | def return_true(): 99 | return True # this comment gets removed accidentally 100 | 101 | 102 | if ( 103 | # huh comment 104 | True 105 | ): 106 | ... 107 | 108 | if ( 109 | # huh 110 | # comment 111 | True 112 | ): 113 | ... 114 | -------------------------------------------------------------------------------- /tests/data/cases/comments_non_breaking_space.py: -------------------------------------------------------------------------------- 1 | from .config import ( ConfigTypeAttributes, Int, Path, # String, 2 | # DEFAULT_TYPE_ATTRIBUTES, 3 | ) 4 | 5 | result = 1 # A simple comment 6 | result = ( 1, ) # Another one 7 | 8 | result = 1 # type: ignore 9 | result = 1# This comment is talking about type: ignore 10 | square = Square(4) # type: Optional[Square] 11 | 12 | def function(a:int=42): 13 | """ This docstring is already formatted 14 | a 15 | b 16 | """ 17 | #  There's a NBSP + 3 spaces before 18 | # And 4 spaces on the next line 19 | pass 20 | 21 | # output 22 | from .config import ( 23 | ConfigTypeAttributes, 24 | Int, 25 | Path, # String, 26 | # DEFAULT_TYPE_ATTRIBUTES, 27 | ) 28 | 29 | result = 1 # A simple comment 30 | result = (1,) # Another one 31 | 32 | result = 1 #  type: ignore 33 | result = 1 # This comment is talking about type: ignore 34 | square = Square(4) #  type: Optional[Square] 35 | 36 | 37 | def function(a: int = 42): 38 | """This docstring is already formatted 39 | a 40 | b 41 | """ 42 | # There's a NBSP + 3 spaces before 43 | # And 4 spaces on the next line 44 | pass 45 | -------------------------------------------------------------------------------- /tests/data/cases/context_managers_38.py: -------------------------------------------------------------------------------- 1 | with \ 2 | make_context_manager1() as cm1, \ 3 | make_context_manager2() as cm2, \ 4 | make_context_manager3() as cm3, \ 5 | make_context_manager4() as cm4 \ 6 | : 7 | pass 8 | 9 | 10 | with \ 11 | make_context_manager1() as cm1, \ 12 | make_context_manager2(), \ 13 | make_context_manager3() as cm3, \ 14 | make_context_manager4() \ 15 | : 16 | pass 17 | 18 | 19 | with \ 20 | new_new_new1() as cm1, \ 21 | new_new_new2() \ 22 | : 23 | pass 24 | 25 | 26 | with mock.patch.object( 27 | self.my_runner, "first_method", autospec=True 28 | ) as mock_run_adb, mock.patch.object( 29 | self.my_runner, "second_method", autospec=True, return_value="foo" 30 | ): 31 | pass 32 | 33 | 34 | # output 35 | 36 | 37 | with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: 38 | pass 39 | 40 | 41 | with make_context_manager1() as cm1, make_context_manager2(), make_context_manager3() as cm3, make_context_manager4(): 42 | pass 43 | 44 | 45 | with new_new_new1() as cm1, new_new_new2(): 46 | pass 47 | 48 | 49 | with mock.patch.object( 50 | self.my_runner, "first_method", autospec=True 51 | ) as mock_run_adb, mock.patch.object( 52 | self.my_runner, "second_method", autospec=True, return_value="foo" 53 | ): 54 | pass 55 | -------------------------------------------------------------------------------- /tests/data/cases/context_managers_autodetect_310.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | # This file uses pattern matching introduced in Python 3.10. 3 | 4 | 5 | match http_code: 6 | case 404: 7 | print("Not found") 8 | 9 | 10 | with \ 11 | make_context_manager1() as cm1, \ 12 | make_context_manager2() as cm2, \ 13 | make_context_manager3() as cm3, \ 14 | make_context_manager4() as cm4 \ 15 | : 16 | pass 17 | 18 | 19 | # output 20 | 21 | 22 | # This file uses pattern matching introduced in Python 3.10. 23 | 24 | 25 | match http_code: 26 | case 404: 27 | print("Not found") 28 | 29 | 30 | with ( 31 | make_context_manager1() as cm1, 32 | make_context_manager2() as cm2, 33 | make_context_manager3() as cm3, 34 | make_context_manager4() as cm4, 35 | ): 36 | pass 37 | -------------------------------------------------------------------------------- /tests/data/cases/context_managers_autodetect_311.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.11 2 | # This file uses except* clause in Python 3.11. 3 | 4 | 5 | try: 6 | some_call() 7 | except* Error as e: 8 | pass 9 | 10 | 11 | with \ 12 | make_context_manager1() as cm1, \ 13 | make_context_manager2() as cm2, \ 14 | make_context_manager3() as cm3, \ 15 | make_context_manager4() as cm4 \ 16 | : 17 | pass 18 | 19 | 20 | # output 21 | 22 | 23 | # This file uses except* clause in Python 3.11. 24 | 25 | 26 | try: 27 | some_call() 28 | except* Error as e: 29 | pass 30 | 31 | 32 | with ( 33 | make_context_manager1() as cm1, 34 | make_context_manager2() as cm2, 35 | make_context_manager3() as cm3, 36 | make_context_manager4() as cm4, 37 | ): 38 | pass 39 | -------------------------------------------------------------------------------- /tests/data/cases/context_managers_autodetect_38.py: -------------------------------------------------------------------------------- 1 | # This file doesn't use any Python 3.9+ only grammars. 2 | 3 | 4 | # Make sure parens around a single context manager don't get autodetected as 5 | # Python 3.9+. 6 | with (a): 7 | pass 8 | 9 | 10 | with \ 11 | make_context_manager1() as cm1, \ 12 | make_context_manager2() as cm2, \ 13 | make_context_manager3() as cm3, \ 14 | make_context_manager4() as cm4 \ 15 | : 16 | pass 17 | 18 | 19 | with mock.patch.object( 20 | self.my_runner, "first_method", autospec=True 21 | ) as mock_run_adb, mock.patch.object( 22 | self.my_runner, "second_method", autospec=True, return_value="foo" 23 | ): 24 | pass 25 | 26 | 27 | # output 28 | # This file doesn't use any Python 3.9+ only grammars. 29 | 30 | 31 | # Make sure parens around a single context manager don't get autodetected as 32 | # Python 3.9+. 33 | with a: 34 | pass 35 | 36 | 37 | with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4: 38 | pass 39 | 40 | 41 | with mock.patch.object( 42 | self.my_runner, "first_method", autospec=True 43 | ) as mock_run_adb, mock.patch.object( 44 | self.my_runner, "second_method", autospec=True, return_value="foo" 45 | ): 46 | pass 47 | -------------------------------------------------------------------------------- /tests/data/cases/context_managers_autodetect_39.py: -------------------------------------------------------------------------------- 1 | # This file uses parenthesized context managers introduced in Python 3.9. 2 | 3 | 4 | with \ 5 | make_context_manager1() as cm1, \ 6 | make_context_manager2() as cm2, \ 7 | make_context_manager3() as cm3, \ 8 | make_context_manager4() as cm4 \ 9 | : 10 | pass 11 | 12 | 13 | with ( 14 | new_new_new1() as cm1, 15 | new_new_new2() 16 | ): 17 | pass 18 | 19 | 20 | # output 21 | # This file uses parenthesized context managers introduced in Python 3.9. 22 | 23 | 24 | with ( 25 | make_context_manager1() as cm1, 26 | make_context_manager2() as cm2, 27 | make_context_manager3() as cm3, 28 | make_context_manager4() as cm4, 29 | ): 30 | pass 31 | 32 | 33 | with new_new_new1() as cm1, new_new_new2(): 34 | pass 35 | -------------------------------------------------------------------------------- /tests/data/cases/docstring_newline.py: -------------------------------------------------------------------------------- 1 | """ 2 | 87 characters ............................................................................ 3 | """ 4 | -------------------------------------------------------------------------------- /tests/data/cases/docstring_no_extra_empty_line_before_eof.py: -------------------------------------------------------------------------------- 1 | # Make sure when the file ends with class's docstring, 2 | # It doesn't add extra blank lines. 3 | class ClassWithDocstring: 4 | """A docstring.""" 5 | -------------------------------------------------------------------------------- /tests/data/cases/f_docstring.py: -------------------------------------------------------------------------------- 1 | def foo(e): 2 | f""" {'.'.join(e)}""" 3 | 4 | def bar(e): 5 | f"{'.'.join(e)}" 6 | 7 | def baz(e): 8 | F""" {'.'.join(e)}""" 9 | 10 | # output 11 | def foo(e): 12 | f""" {'.'.join(e)}""" 13 | 14 | 15 | def bar(e): 16 | f"{'.'.join(e)}" 17 | 18 | 19 | def baz(e): 20 | f""" {'.'.join(e)}""" 21 | -------------------------------------------------------------------------------- /tests/data/cases/fmtonoff2.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | TmSt = 1 4 | TmEx = 2 5 | 6 | # fmt: off 7 | 8 | # Test data: 9 | # Position, Volume, State, TmSt/TmEx/None, [call, [arg1...]] 10 | 11 | @pytest.mark.parametrize('test', [ 12 | 13 | # Test don't manage the volume 14 | [ 15 | ('stuff', 'in') 16 | ], 17 | ]) 18 | def test_fader(test): 19 | pass 20 | 21 | def check_fader(test): 22 | 23 | pass 24 | 25 | def verify_fader(test): 26 | # misaligned comment 27 | pass 28 | 29 | def verify_fader(test): 30 | """Hey, ho.""" 31 | assert test.passed() 32 | 33 | def test_calculate_fades(): 34 | calcs = [ 35 | # one is zero/none 36 | (0, 4, 0, 0, 10, 0, 0, 6, 10), 37 | (None, 4, 0, 0, 10, 0, 0, 6, 10), 38 | ] 39 | 40 | # fmt: on 41 | -------------------------------------------------------------------------------- /tests/data/cases/fmtonoff3.py: -------------------------------------------------------------------------------- 1 | # fmt: off 2 | x = [ 3 | 1, 2, 4 | 3, 4, 5 | ] 6 | # fmt: on 7 | 8 | # fmt: off 9 | x = [ 10 | 1, 2, 11 | 3, 4, 12 | ] 13 | # fmt: on 14 | 15 | x = [ 16 | 1, 2, 3, 4 17 | ] 18 | 19 | # output 20 | 21 | # fmt: off 22 | x = [ 23 | 1, 2, 24 | 3, 4, 25 | ] 26 | # fmt: on 27 | 28 | # fmt: off 29 | x = [ 30 | 1, 2, 31 | 3, 4, 32 | ] 33 | # fmt: on 34 | 35 | x = [1, 2, 3, 4] 36 | -------------------------------------------------------------------------------- /tests/data/cases/fmtonoff4.py: -------------------------------------------------------------------------------- 1 | # fmt: off 2 | @test([ 3 | 1, 2, 4 | 3, 4, 5 | ]) 6 | # fmt: on 7 | def f(): pass 8 | 9 | @test([ 10 | 1, 2, 11 | 3, 4, 12 | ]) 13 | def f(): pass 14 | 15 | # output 16 | 17 | # fmt: off 18 | @test([ 19 | 1, 2, 20 | 3, 4, 21 | ]) 22 | # fmt: on 23 | def f(): 24 | pass 25 | 26 | 27 | @test( 28 | [ 29 | 1, 30 | 2, 31 | 3, 32 | 4, 33 | ] 34 | ) 35 | def f(): 36 | pass 37 | -------------------------------------------------------------------------------- /tests/data/cases/fmtonoff6.py: -------------------------------------------------------------------------------- 1 | # Regression test for https://github.com/psf/black/issues/2478. 2 | def foo(): 3 | arr = ( 4 | (3833567325051000, 5, 1, 2, 4229.25, 6, 0), 5 | # fmt: off 6 | ) 7 | 8 | 9 | # Regression test for https://github.com/psf/black/issues/3458. 10 | dependencies = { 11 | a: b, 12 | # fmt: off 13 | } 14 | -------------------------------------------------------------------------------- /tests/data/cases/fmtpass_imports.py: -------------------------------------------------------------------------------- 1 | # Regression test for https://github.com/psf/black/issues/3438 2 | 3 | import ast 4 | import collections # fmt: skip 5 | import dataclasses 6 | # fmt: off 7 | import os 8 | # fmt: on 9 | import pathlib 10 | 11 | import re # fmt: skip 12 | import secrets 13 | 14 | # fmt: off 15 | import sys 16 | # fmt: on 17 | 18 | import tempfile 19 | import zoneinfo 20 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip.py: -------------------------------------------------------------------------------- 1 | a, b = 1, 2 2 | c = 6 # fmt: skip 3 | d = 5 4 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip10.py: -------------------------------------------------------------------------------- 1 | # flags: --preview 2 | def foo(): return "mock" # fmt: skip 3 | if True: print("yay") # fmt: skip 4 | for i in range(10): print(i) # fmt: skip 5 | 6 | j = 1 # fmt: skip 7 | while j < 10: j += 1 # fmt: skip 8 | 9 | b = [c for c in "A very long string that would normally generate some kind of collapse, since it is this long"] # fmt: skip 10 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip11.py: -------------------------------------------------------------------------------- 1 | def foo(): 2 | pass 3 | 4 | 5 | # comment 1 # fmt: skip 6 | # comment 2 7 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip2.py: -------------------------------------------------------------------------------- 1 | # flags: --no-preview-line-length-1 2 | # l2 loses the comment with line-length=1 in preview mode 3 | l1 = ["This list should be broken up", "into multiple lines", "because it is way too long"] 4 | l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip 5 | l3 = ["I have", "trailing comma", "so I should be braked",] 6 | 7 | # output 8 | 9 | # l2 loses the comment with line-length=1 in preview mode 10 | l1 = [ 11 | "This list should be broken up", 12 | "into multiple lines", 13 | "because it is way too long", 14 | ] 15 | l2 = ["But this list shouldn't", "even though it also has", "way too many characters in it"] # fmt: skip 16 | l3 = [ 17 | "I have", 18 | "trailing comma", 19 | "so I should be braked", 20 | ] 21 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip3.py: -------------------------------------------------------------------------------- 1 | a = 3 2 | # fmt: off 3 | b, c = 1, 2 4 | d = 6 # fmt: skip 5 | e = 5 6 | # fmt: on 7 | f = ["This is a very long line that should be formatted into a clearer line ", "by rearranging."] 8 | 9 | # output 10 | 11 | a = 3 12 | # fmt: off 13 | b, c = 1, 2 14 | d = 6 # fmt: skip 15 | e = 5 16 | # fmt: on 17 | f = [ 18 | "This is a very long line that should be formatted into a clearer line ", 19 | "by rearranging.", 20 | ] 21 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip4.py: -------------------------------------------------------------------------------- 1 | a = 2 2 | # fmt: skip 3 | l = [1, 2, 3,] 4 | 5 | # output 6 | 7 | a = 2 8 | # fmt: skip 9 | l = [ 10 | 1, 11 | 2, 12 | 3, 13 | ] -------------------------------------------------------------------------------- /tests/data/cases/fmtskip5.py: -------------------------------------------------------------------------------- 1 | a, b, c = 3, 4, 5 2 | if ( 3 | a == 3 4 | and b != 9 # fmt: skip 5 | and c is not None 6 | ): 7 | print("I'm good!") 8 | else: 9 | print("I'm bad") 10 | 11 | 12 | # output 13 | 14 | a, b, c = 3, 4, 5 15 | if ( 16 | a == 3 17 | and b != 9 # fmt: skip 18 | and c is not None 19 | ): 20 | print("I'm good!") 21 | else: 22 | print("I'm bad") 23 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip6.py: -------------------------------------------------------------------------------- 1 | class A: 2 | def f(self): 3 | for line in range(10): 4 | if True: 5 | pass # fmt: skip 6 | 7 | # output 8 | 9 | class A: 10 | def f(self): 11 | for line in range(10): 12 | if True: 13 | pass # fmt: skip 14 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip7.py: -------------------------------------------------------------------------------- 1 | a = "this is some code" 2 | b = 5 #fmt:skip 3 | c = 9 #fmt: skip 4 | d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip 5 | 6 | # output 7 | 8 | a = "this is some code" 9 | b = 5 # fmt:skip 10 | c = 9 # fmt: skip 11 | d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip 12 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip8.py: -------------------------------------------------------------------------------- 1 | # Make sure a leading comment is not removed. 2 | def some_func( unformatted, args ): # fmt: skip 3 | print("I am some_func") 4 | return 0 5 | # Make sure this comment is not removed. 6 | 7 | 8 | # Make sure a leading comment is not removed. 9 | async def some_async_func( unformatted, args): # fmt: skip 10 | print("I am some_async_func") 11 | await asyncio.sleep(1) 12 | 13 | 14 | # Make sure a leading comment is not removed. 15 | class SomeClass( Unformatted, SuperClasses ): # fmt: skip 16 | def some_method( self, unformatted, args ): # fmt: skip 17 | print("I am some_method") 18 | return 0 19 | 20 | async def some_async_method( self, unformatted, args ): # fmt: skip 21 | print("I am some_async_method") 22 | await asyncio.sleep(1) 23 | 24 | 25 | # Make sure a leading comment is not removed. 26 | if unformatted_call( args ): # fmt: skip 27 | print("First branch") 28 | # Make sure this is not removed. 29 | elif another_unformatted_call( args ): # fmt: skip 30 | print("Second branch") 31 | else : # fmt: skip 32 | print("Last branch") 33 | 34 | 35 | while some_condition( unformatted, args ): # fmt: skip 36 | print("Do something") 37 | 38 | 39 | for i in some_iter( unformatted, args ): # fmt: skip 40 | print("Do something") 41 | 42 | 43 | async def test_async_for(): 44 | async for i in some_async_iter( unformatted, args ): # fmt: skip 45 | print("Do something") 46 | 47 | 48 | try : # fmt: skip 49 | some_call() 50 | except UnformattedError as ex: # fmt: skip 51 | handle_exception() 52 | finally : # fmt: skip 53 | finally_call() 54 | 55 | 56 | with give_me_context( unformatted, args ): # fmt: skip 57 | print("Do something") 58 | 59 | 60 | async def test_async_with(): 61 | async with give_me_async_context( unformatted, args ): # fmt: skip 62 | print("Do something") 63 | -------------------------------------------------------------------------------- /tests/data/cases/fmtskip9.py: -------------------------------------------------------------------------------- 1 | print () # fmt: skip 2 | print () # fmt:skip 3 | 4 | 5 | # output 6 | 7 | print () # fmt: skip 8 | print () # fmt:skip 9 | -------------------------------------------------------------------------------- /tests/data/cases/format_unicode_escape_seq.py: -------------------------------------------------------------------------------- 1 | x = "\x1F" 2 | x = "\\x1B" 3 | x = "\\\x1B" 4 | x = "\U0001F60E" 5 | x = "\u0001F60E" 6 | x = r"\u0001F60E" 7 | x = "don't format me" 8 | x = "\xA3" 9 | x = "\u2717" 10 | x = "\uFaCe" 11 | x = "\N{ox}\N{OX}" 12 | x = "\N{lAtIn smaLL letteR x}" 13 | x = "\N{CYRILLIC small LETTER BYELORUSSIAN-UKRAINIAN I}" 14 | x = b"\x1Fdon't byte" 15 | x = rb"\x1Fdon't format" 16 | 17 | # output 18 | 19 | x = "\x1f" 20 | x = "\\x1B" 21 | x = "\\\x1b" 22 | x = "\U0001f60e" 23 | x = "\u0001F60E" 24 | x = r"\u0001F60E" 25 | x = "don't format me" 26 | x = "\xa3" 27 | x = "\u2717" 28 | x = "\uface" 29 | x = "\N{OX}\N{OX}" 30 | x = "\N{LATIN SMALL LETTER X}" 31 | x = "\N{CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I}" 32 | x = b"\x1fdon't byte" 33 | x = rb"\x1Fdon't format" 34 | -------------------------------------------------------------------------------- /tests/data/cases/fstring.py: -------------------------------------------------------------------------------- 1 | f"f-string without formatted values is just a string" 2 | f"{{NOT a formatted value}}" 3 | f"{{NOT 'a' \"formatted\" \"value\"}}" 4 | f"some f-string with {a} {few():.2f} {formatted.values!r}" 5 | f'some f-string with {a} {few(""):.2f} {formatted.values!r}' 6 | f"{f'''{'nested'} inner'''} outer" 7 | f"\"{f'{nested} inner'}\" outer" 8 | f"space between opening braces: { {a for a in (1, 2, 3)}}" 9 | f'Hello \'{tricky + "example"}\'' 10 | f"Tried directories {str(rootdirs)} \ 11 | but none started with prefix {parentdir_prefix}" 12 | 13 | # output 14 | 15 | f"f-string without formatted values is just a string" 16 | f"{{NOT a formatted value}}" 17 | f'{{NOT \'a\' "formatted" "value"}}' 18 | f"some f-string with {a} {few():.2f} {formatted.values!r}" 19 | f'some f-string with {a} {few(""):.2f} {formatted.values!r}' 20 | f"{f'''{'nested'} inner'''} outer" 21 | f"\"{f'{nested} inner'}\" outer" 22 | f"space between opening braces: { {a for a in (1, 2, 3)}}" 23 | f'Hello \'{tricky + "example"}\'' 24 | f"Tried directories {str(rootdirs)} \ 25 | but none started with prefix {parentdir_prefix}" 26 | -------------------------------------------------------------------------------- /tests/data/cases/fstring_quotations.py: -------------------------------------------------------------------------------- 1 | # Regression tests for long f-strings, including examples from issue #3623 2 | 3 | a = ( 4 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 5 | f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 6 | ) 7 | 8 | a = ( 9 | f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 10 | 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' 11 | ) 12 | 13 | a = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + \ 14 | f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 15 | 16 | a = f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' + \ 17 | f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 18 | 19 | a = ( 20 | f'bbbbbbb"{"b"}"' 21 | 'aaaaaaaa' 22 | ) 23 | 24 | a = ( 25 | f'"{"b"}"' 26 | ) 27 | 28 | a = ( 29 | f'\"{"b"}\"' 30 | ) 31 | 32 | a = ( 33 | r'\"{"b"}\"' 34 | ) 35 | 36 | # output 37 | 38 | # Regression tests for long f-strings, including examples from issue #3623 39 | 40 | a = ( 41 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 42 | f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 43 | ) 44 | 45 | a = ( 46 | f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 47 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 48 | ) 49 | 50 | a = ( 51 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 52 | + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 53 | ) 54 | 55 | a = ( 56 | f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 57 | + f'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"{"b"}"' 58 | ) 59 | 60 | a = f'bbbbbbb"{"b"}"' "aaaaaaaa" 61 | 62 | a = f'"{"b"}"' 63 | 64 | a = f'"{"b"}"' 65 | 66 | a = r'\"{"b"}\"' 67 | 68 | -------------------------------------------------------------------------------- /tests/data/cases/ignore_pyi.py: -------------------------------------------------------------------------------- 1 | # flags: --pyi 2 | def f(): # type: ignore 3 | ... 4 | 5 | class x: # some comment 6 | ... 7 | 8 | class y: 9 | ... # comment 10 | 11 | # whitespace doesn't matter (note the next line has a trailing space and tab) 12 | class z: 13 | ... 14 | 15 | def g(): 16 | # hi 17 | ... 18 | 19 | def h(): 20 | ... 21 | # bye 22 | 23 | # output 24 | 25 | def f(): # type: ignore 26 | ... 27 | 28 | class x: # some comment 29 | ... 30 | 31 | class y: ... # comment 32 | 33 | # whitespace doesn't matter (note the next line has a trailing space and tab) 34 | class z: ... 35 | 36 | def g(): 37 | # hi 38 | ... 39 | 40 | def h(): 41 | ... 42 | # bye 43 | -------------------------------------------------------------------------------- /tests/data/cases/is_simple_lookup_for_doublestar_expression.py: -------------------------------------------------------------------------------- 1 | m2 = None if not isinstance(dist, Normal) else m** 2 + s * 2 2 | m3 = None if not isinstance(dist, Normal) else m ** 2 + s * 2 3 | m4 = None if not isinstance(dist, Normal) else m**2 + s * 2 4 | m5 = obj.method(another_obj.method()).attribute **2 5 | m6 = None if ... else m**2 + s**2 6 | 7 | 8 | # output 9 | m2 = None if not isinstance(dist, Normal) else m**2 + s * 2 10 | m3 = None if not isinstance(dist, Normal) else m**2 + s * 2 11 | m4 = None if not isinstance(dist, Normal) else m**2 + s * 2 12 | m5 = obj.method(another_obj.method()).attribute ** 2 13 | m6 = None if ... else m**2 + s**2 -------------------------------------------------------------------------------- /tests/data/cases/keep_newline_after_match.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | def http_status(status): 3 | 4 | match status: 5 | 6 | case 400: 7 | 8 | return "Bad request" 9 | 10 | case 401: 11 | 12 | return "Unauthorized" 13 | 14 | case 403: 15 | 16 | return "Forbidden" 17 | 18 | case 404: 19 | 20 | return "Not found" 21 | 22 | # output 23 | def http_status(status): 24 | 25 | match status: 26 | 27 | case 400: 28 | 29 | return "Bad request" 30 | 31 | case 401: 32 | 33 | return "Unauthorized" 34 | 35 | case 403: 36 | 37 | return "Forbidden" 38 | 39 | case 404: 40 | 41 | return "Not found" -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_diff_edge_case.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=10-11 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | 5 | # Reproducible example for https://github.com/psf/black/issues/4033. 6 | # This can be fixed in the future if we use a better diffing algorithm, or make Black 7 | # perform formatting in a single pass. 8 | 9 | print ( "format me" ) 10 | print ( "format me" ) 11 | print ( "format me" ) 12 | print ( "format me" ) 13 | print ( "format me" ) 14 | 15 | # output 16 | # flags: --line-ranges=10-11 17 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 18 | # flag above as it's formatting specifically these lines. 19 | 20 | # Reproducible example for https://github.com/psf/black/issues/4033. 21 | # This can be fixed in the future if we use a better diffing algorithm, or make Black 22 | # perform formatting in a single pass. 23 | 24 | print ( "format me" ) 25 | print("format me") 26 | print("format me") 27 | print("format me") 28 | print("format me") 29 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_exceeding_end.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=6-1000 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 5 | def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 6 | def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 7 | def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 8 | 9 | # output 10 | # flags: --line-ranges=6-1000 11 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 12 | # flag above as it's formatting specifically these lines. 13 | def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 14 | def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 15 | def foo3( 16 | parameter_1, 17 | parameter_2, 18 | parameter_3, 19 | parameter_4, 20 | parameter_5, 21 | parameter_6, 22 | parameter_7, 23 | ): 24 | pass 25 | 26 | 27 | def foo4( 28 | parameter_1, 29 | parameter_2, 30 | parameter_3, 31 | parameter_4, 32 | parameter_5, 33 | parameter_6, 34 | parameter_7, 35 | ): 36 | pass 37 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_fmt_off.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=7-7 --line-ranges=17-23 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | 5 | # fmt: off 6 | import os 7 | def myfunc( ): # Intentionally unformatted. 8 | pass 9 | # fmt: on 10 | 11 | 12 | def myfunc( ): # This will not be reformatted. 13 | print( {"also won't be reformatted"} ) 14 | # fmt: off 15 | def myfunc( ): # This will not be reformatted. 16 | print( {"also won't be reformatted"} ) 17 | def myfunc( ): # This will not be reformatted. 18 | print( {"also won't be reformatted"} ) 19 | # fmt: on 20 | 21 | 22 | def myfunc( ): # This will be reformatted. 23 | print( {"this will be reformatted"} ) 24 | 25 | # output 26 | 27 | # flags: --line-ranges=7-7 --line-ranges=17-23 28 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 29 | # flag above as it's formatting specifically these lines. 30 | 31 | # fmt: off 32 | import os 33 | def myfunc( ): # Intentionally unformatted. 34 | pass 35 | # fmt: on 36 | 37 | 38 | def myfunc( ): # This will not be reformatted. 39 | print( {"also won't be reformatted"} ) 40 | # fmt: off 41 | def myfunc( ): # This will not be reformatted. 42 | print( {"also won't be reformatted"} ) 43 | def myfunc( ): # This will not be reformatted. 44 | print( {"also won't be reformatted"} ) 45 | # fmt: on 46 | 47 | 48 | def myfunc(): # This will be reformatted. 49 | print({"this will be reformatted"}) 50 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_fmt_off_decorator.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=12-12 --line-ranges=21-21 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | 5 | # Regression test for an edge case involving decorators and fmt: off/on. 6 | class MyClass: 7 | 8 | # fmt: off 9 | @decorator ( ) 10 | # fmt: on 11 | def method(): 12 | print ( "str" ) 13 | 14 | @decor( 15 | a=1, 16 | # fmt: off 17 | b=(2, 3), 18 | # fmt: on 19 | ) 20 | def func(): 21 | pass 22 | 23 | 24 | # output 25 | 26 | # flags: --line-ranges=12-12 --line-ranges=21-21 27 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 28 | # flag above as it's formatting specifically these lines. 29 | 30 | # Regression test for an edge case involving decorators and fmt: off/on. 31 | class MyClass: 32 | 33 | # fmt: off 34 | @decorator ( ) 35 | # fmt: on 36 | def method(): 37 | print("str") 38 | 39 | @decor( 40 | a=1, 41 | # fmt: off 42 | b=(2, 3), 43 | # fmt: on 44 | ) 45 | def func(): 46 | pass 47 | 48 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_fmt_off_overlap.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=11-17 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | 5 | 6 | def myfunc( ): # This will not be reformatted. 7 | print( {"also won't be reformatted"} ) 8 | # fmt: off 9 | def myfunc( ): # This will not be reformatted. 10 | print( {"also won't be reformatted"} ) 11 | def myfunc( ): # This will not be reformatted. 12 | print( {"also won't be reformatted"} ) 13 | # fmt: on 14 | 15 | 16 | def myfunc( ): # This will be reformatted. 17 | print( {"this will be reformatted"} ) 18 | 19 | # output 20 | 21 | # flags: --line-ranges=11-17 22 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 23 | # flag above as it's formatting specifically these lines. 24 | 25 | 26 | def myfunc( ): # This will not be reformatted. 27 | print( {"also won't be reformatted"} ) 28 | # fmt: off 29 | def myfunc( ): # This will not be reformatted. 30 | print( {"also won't be reformatted"} ) 31 | def myfunc( ): # This will not be reformatted. 32 | print( {"also won't be reformatted"} ) 33 | # fmt: on 34 | 35 | 36 | def myfunc(): # This will be reformatted. 37 | print({"this will be reformatted"}) 38 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_imports.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=8-8 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | 5 | # This test ensures no empty lines are added around import lines. 6 | # It caused an issue before https://github.com/psf/black/pull/3610 is merged. 7 | import os 8 | import re 9 | import sys 10 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_indentation.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=5-5 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | if cond1: 5 | print("first") 6 | if cond2: 7 | print("second") 8 | else: 9 | print("else") 10 | 11 | if another_cond: 12 | print("will not be changed") 13 | 14 | # output 15 | 16 | # flags: --line-ranges=5-5 17 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 18 | # flag above as it's formatting specifically these lines. 19 | if cond1: 20 | print("first") 21 | if cond2: 22 | print("second") 23 | else: 24 | print("else") 25 | 26 | if another_cond: 27 | print("will not be changed") 28 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_outside_source.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=5000-6000 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines, in this case none. 4 | def foo1(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 5 | def foo2(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 6 | def foo3(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 7 | def foo4(parameter_1, parameter_2, parameter_3, parameter_4, parameter_5, parameter_6, parameter_7): pass 8 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_two_passes.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=9-11 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | 5 | # This is a specific case for Black's two-pass formatting behavior in `format_str`. 6 | # The second pass must respect the line ranges before the first pass. 7 | 8 | 9 | def restrict_to_this_line(arg1, 10 | arg2, 11 | arg3): 12 | print ( "This should not be formatted." ) 13 | print ( "Note that in the second pass, the original line range 9-11 will cover these print lines.") 14 | 15 | # output 16 | 17 | # flags: --line-ranges=9-11 18 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 19 | # flag above as it's formatting specifically these lines. 20 | 21 | # This is a specific case for Black's two-pass formatting behavior in `format_str`. 22 | # The second pass must respect the line ranges before the first pass. 23 | 24 | 25 | def restrict_to_this_line(arg1, arg2, arg3): 26 | print ( "This should not be formatted." ) 27 | print ( "Note that in the second pass, the original line range 9-11 will cover these print lines.") 28 | -------------------------------------------------------------------------------- /tests/data/cases/line_ranges_unwrapping.py: -------------------------------------------------------------------------------- 1 | # flags: --line-ranges=5-5 --line-ranges=9-9 --line-ranges=13-13 2 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 3 | # flag above as it's formatting specifically these lines. 4 | alist = [ 5 | 1, 2 6 | ] 7 | 8 | adict = { 9 | "key" : "value" 10 | } 11 | 12 | func_call ( 13 | arg = value 14 | ) 15 | 16 | # output 17 | 18 | # flags: --line-ranges=5-5 --line-ranges=9-9 --line-ranges=13-13 19 | # NOTE: If you need to modify this file, pay special attention to the --line-ranges= 20 | # flag above as it's formatting specifically these lines. 21 | alist = [1, 2] 22 | 23 | adict = {"key": "value"} 24 | 25 | func_call(arg=value) 26 | -------------------------------------------------------------------------------- /tests/data/cases/linelength6.py: -------------------------------------------------------------------------------- 1 | # flags: --line-length=6 2 | # Regression test for #3427, which reproes only with line length <= 6 3 | def f(): 4 | """ 5 | x 6 | """ 7 | -------------------------------------------------------------------------------- /tests/data/cases/long_strings__type_annotations.py: -------------------------------------------------------------------------------- 1 | def func( 2 | arg1, 3 | arg2, 4 | ) -> Set["this_is_a_very_long_module_name.AndAVeryLongClasName" 5 | ".WithAVeryVeryVeryVeryVeryLongSubClassName"]: 6 | pass 7 | 8 | 9 | def func( 10 | argument: ( 11 | "VeryLongClassNameWithAwkwardGenericSubtype[int] |" 12 | "VeryLongClassNameWithAwkwardGenericSubtype[str]" 13 | ), 14 | ) -> ( 15 | "VeryLongClassNameWithAwkwardGenericSubtype[int] |" 16 | "VeryLongClassNameWithAwkwardGenericSubtype[str]" 17 | ): 18 | pass 19 | 20 | 21 | def func( 22 | argument: ( 23 | "int |" 24 | "str" 25 | ), 26 | ) -> Set["int |" 27 | " str"]: 28 | pass 29 | 30 | 31 | # output 32 | 33 | 34 | def func( 35 | arg1, 36 | arg2, 37 | ) -> Set[ 38 | "this_is_a_very_long_module_name.AndAVeryLongClasName" 39 | ".WithAVeryVeryVeryVeryVeryLongSubClassName" 40 | ]: 41 | pass 42 | 43 | 44 | def func( 45 | argument: ( 46 | "VeryLongClassNameWithAwkwardGenericSubtype[int] |" 47 | "VeryLongClassNameWithAwkwardGenericSubtype[str]" 48 | ), 49 | ) -> ( 50 | "VeryLongClassNameWithAwkwardGenericSubtype[int] |" 51 | "VeryLongClassNameWithAwkwardGenericSubtype[str]" 52 | ): 53 | pass 54 | 55 | 56 | def func( 57 | argument: "int |" "str", 58 | ) -> Set["int |" " str"]: 59 | pass 60 | -------------------------------------------------------------------------------- /tests/data/cases/module_docstring_1.py: -------------------------------------------------------------------------------- 1 | """Single line module-level docstring should be followed by single newline.""" 2 | 3 | 4 | 5 | 6 | a = 1 7 | 8 | 9 | """I'm just a string so should be followed by 2 newlines.""" 10 | 11 | 12 | 13 | 14 | b = 2 15 | 16 | # output 17 | """Single line module-level docstring should be followed by single newline.""" 18 | 19 | a = 1 20 | 21 | 22 | """I'm just a string so should be followed by 2 newlines.""" 23 | 24 | 25 | b = 2 26 | -------------------------------------------------------------------------------- /tests/data/cases/module_docstring_2.py: -------------------------------------------------------------------------------- 1 | """I am a very helpful module docstring. 2 | 3 | With trailing spaces: 4 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, 5 | sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 6 | Ut enim ad minim veniam, 7 | quis nostrud exercitation ullamco laboris 8 | nisi ut aliquip ex ea commodo consequat. 9 | Duis aute irure dolor in reprehenderit in voluptate 10 | velit esse cillum dolore eu fugiat nulla pariatur. 11 | Excepteur sint occaecat cupidatat non proident, 12 | sunt in culpa qui officia deserunt mollit anim id est laborum. 13 | """ 14 | 15 | 16 | 17 | 18 | a = 1 19 | 20 | 21 | """Look at me I'm a docstring... 22 | 23 | ............................................................ 24 | ............................................................ 25 | ............................................................ 26 | ............................................................ 27 | ............................................................ 28 | ............................................................ 29 | ............................................................ 30 | ........................................................NOT! 31 | """ 32 | 33 | 34 | 35 | 36 | b = 2 37 | 38 | # output 39 | """I am a very helpful module docstring. 40 | 41 | With trailing spaces: 42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, 43 | sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 44 | Ut enim ad minim veniam, 45 | quis nostrud exercitation ullamco laboris 46 | nisi ut aliquip ex ea commodo consequat. 47 | Duis aute irure dolor in reprehenderit in voluptate 48 | velit esse cillum dolore eu fugiat nulla pariatur. 49 | Excepteur sint occaecat cupidatat non proident, 50 | sunt in culpa qui officia deserunt mollit anim id est laborum. 51 | """ 52 | 53 | a = 1 54 | 55 | 56 | """Look at me I'm a docstring... 57 | 58 | ............................................................ 59 | ............................................................ 60 | ............................................................ 61 | ............................................................ 62 | ............................................................ 63 | ............................................................ 64 | ............................................................ 65 | ........................................................NOT! 66 | """ 67 | 68 | 69 | b = 2 70 | -------------------------------------------------------------------------------- /tests/data/cases/module_docstring_3.py: -------------------------------------------------------------------------------- 1 | """Single line module-level docstring should be followed by single newline.""" 2 | a = 1 3 | 4 | # output 5 | """Single line module-level docstring should be followed by single newline.""" 6 | 7 | a = 1 8 | -------------------------------------------------------------------------------- /tests/data/cases/module_docstring_4.py: -------------------------------------------------------------------------------- 1 | """Single line module-level docstring should be followed by single newline.""" 2 | 3 | a = 1 4 | 5 | # output 6 | """Single line module-level docstring should be followed by single newline.""" 7 | 8 | a = 1 9 | -------------------------------------------------------------------------------- /tests/data/cases/module_docstring_followed_by_class.py: -------------------------------------------------------------------------------- 1 | """Two blank lines between module docstring and a class.""" 2 | class MyClass: 3 | pass 4 | 5 | # output 6 | """Two blank lines between module docstring and a class.""" 7 | 8 | 9 | class MyClass: 10 | pass 11 | -------------------------------------------------------------------------------- /tests/data/cases/module_docstring_followed_by_function.py: -------------------------------------------------------------------------------- 1 | """Two blank lines between module docstring and a function def.""" 2 | def function(): 3 | pass 4 | 5 | # output 6 | """Two blank lines between module docstring and a function def.""" 7 | 8 | 9 | def function(): 10 | pass 11 | -------------------------------------------------------------------------------- /tests/data/cases/multiline_consecutive_open_parentheses_ignore.py: -------------------------------------------------------------------------------- 1 | # This is a regression test. Issue #3737 2 | 3 | a = ( # type: ignore 4 | int( # type: ignore 5 | int( # type: ignore 6 | int( # type: ignore 7 | 6 8 | ) 9 | ) 10 | ) 11 | ) 12 | 13 | b = ( 14 | int( 15 | 6 16 | ) 17 | ) 18 | 19 | print( "111") # type: ignore 20 | print( "111" ) # type: ignore 21 | print( "111" ) # type: ignore 22 | 23 | 24 | # output 25 | 26 | 27 | # This is a regression test. Issue #3737 28 | 29 | a = ( # type: ignore 30 | int( # type: ignore 31 | int( # type: ignore 32 | int(6) # type: ignore 33 | ) 34 | ) 35 | ) 36 | 37 | b = int(6) 38 | 39 | print("111") # type: ignore 40 | print("111") # type: ignore 41 | print("111") # type: ignore -------------------------------------------------------------------------------- /tests/data/cases/nested_stub.py: -------------------------------------------------------------------------------- 1 | # flags: --pyi 2 | import sys 3 | 4 | class Outer: 5 | class InnerStub: ... 6 | outer_attr_after_inner_stub: int 7 | class Inner: 8 | inner_attr: int 9 | outer_attr: int 10 | 11 | if sys.version_info > (3, 7): 12 | if sys.platform == "win32": 13 | assignment = 1 14 | def function_definition(self): ... 15 | def f1(self) -> str: ... 16 | if sys.platform != "win32": 17 | def function_definition(self): ... 18 | assignment = 1 19 | def f2(self) -> str: ... 20 | 21 | 22 | class TopLevel: 23 | class Nested1: 24 | foo: int 25 | def bar(self): ... 26 | field = 1 27 | 28 | class Nested2: 29 | def bar(self): ... 30 | foo: int 31 | field = 1 32 | 33 | # output 34 | 35 | import sys 36 | 37 | class Outer: 38 | class InnerStub: ... 39 | outer_attr_after_inner_stub: int 40 | 41 | class Inner: 42 | inner_attr: int 43 | 44 | outer_attr: int 45 | 46 | if sys.version_info > (3, 7): 47 | if sys.platform == "win32": 48 | assignment = 1 49 | def function_definition(self): ... 50 | 51 | def f1(self) -> str: ... 52 | if sys.platform != "win32": 53 | def function_definition(self): ... 54 | assignment = 1 55 | 56 | def f2(self) -> str: ... 57 | 58 | class TopLevel: 59 | class Nested1: 60 | foo: int 61 | def bar(self): ... 62 | 63 | field = 1 64 | 65 | class Nested2: 66 | def bar(self): ... 67 | foo: int 68 | 69 | field = 1 70 | -------------------------------------------------------------------------------- /tests/data/cases/no_blank_line_before_docstring.py: -------------------------------------------------------------------------------- 1 | def line_before_docstring(): 2 | 3 | """Please move me up""" 4 | 5 | 6 | class LineBeforeDocstring: 7 | 8 | """Please move me up""" 9 | 10 | 11 | class EvenIfThereIsAMethodAfter: 12 | 13 | """I'm the docstring""" 14 | def method(self): 15 | pass 16 | 17 | 18 | class TwoLinesBeforeDocstring: 19 | 20 | 21 | """I want to be treated the same as if I were closer""" 22 | 23 | 24 | class MultilineDocstringsAsWell: 25 | 26 | """I'm so far 27 | 28 | and on so many lines... 29 | """ 30 | 31 | class SingleQuotedDocstring: 32 | 33 | "I'm a docstring but I don't even get triple quotes." 34 | 35 | # output 36 | 37 | 38 | def line_before_docstring(): 39 | """Please move me up""" 40 | 41 | 42 | class LineBeforeDocstring: 43 | """Please move me up""" 44 | 45 | 46 | class EvenIfThereIsAMethodAfter: 47 | """I'm the docstring""" 48 | 49 | def method(self): 50 | pass 51 | 52 | 53 | class TwoLinesBeforeDocstring: 54 | """I want to be treated the same as if I were closer""" 55 | 56 | 57 | class MultilineDocstringsAsWell: 58 | """I'm so far 59 | 60 | and on so many lines... 61 | """ 62 | 63 | 64 | class SingleQuotedDocstring: 65 | "I'm a docstring but I don't even get triple quotes." 66 | -------------------------------------------------------------------------------- /tests/data/cases/numeric_literals.py: -------------------------------------------------------------------------------- 1 | x = 123456789 2 | x = 123456 3 | x = .1 4 | x = 1. 5 | x = 1E+1 6 | x = 1E-1 7 | x = 1.000_000_01 8 | x = 123456789.123456789 9 | x = 123456789.123456789E123456789 10 | x = 123456789E123456789 11 | x = 123456789J 12 | x = 123456789.123456789J 13 | x = 0XB1ACC 14 | x = 0B1011 15 | x = 0O777 16 | x = 0.000000006 17 | x = 10000 18 | x = 133333 19 | 20 | # output 21 | 22 | x = 123456789 23 | x = 123456 24 | x = 0.1 25 | x = 1.0 26 | x = 1e1 27 | x = 1e-1 28 | x = 1.000_000_01 29 | x = 123456789.123456789 30 | x = 123456789.123456789e123456789 31 | x = 123456789e123456789 32 | x = 123456789j 33 | x = 123456789.123456789j 34 | x = 0xB1ACC 35 | x = 0b1011 36 | x = 0o777 37 | x = 0.000000006 38 | x = 10000 39 | x = 133333 40 | -------------------------------------------------------------------------------- /tests/data/cases/numeric_literals_skip_underscores.py: -------------------------------------------------------------------------------- 1 | x = 123456789 2 | x = 1_2_3_4_5_6_7 3 | x = 1E+1 4 | x = 0xb1acc 5 | x = 0.00_00_006 6 | x = 12_34_567J 7 | x = .1_2 8 | x = 1_2. 9 | 10 | # output 11 | 12 | x = 123456789 13 | x = 1_2_3_4_5_6_7 14 | x = 1e1 15 | x = 0xB1ACC 16 | x = 0.00_00_006 17 | x = 12_34_567j 18 | x = 0.1_2 19 | x = 1_2.0 -------------------------------------------------------------------------------- /tests/data/cases/one_element_subscript.py: -------------------------------------------------------------------------------- 1 | # We should not treat the trailing comma 2 | # in a single-element subscript. 3 | a: tuple[int,] 4 | b = tuple[int,] 5 | 6 | # The magic comma still applies to multi-element subscripts. 7 | c: tuple[int, int,] 8 | d = tuple[int, int,] 9 | 10 | # Magic commas still work as expected for non-subscripts. 11 | small_list = [1,] 12 | list_of_types = [tuple[int,],] 13 | 14 | # output 15 | # We should not treat the trailing comma 16 | # in a single-element subscript. 17 | a: tuple[int,] 18 | b = tuple[int,] 19 | 20 | # The magic comma still applies to multi-element subscripts. 21 | c: tuple[ 22 | int, 23 | int, 24 | ] 25 | d = tuple[ 26 | int, 27 | int, 28 | ] 29 | 30 | # Magic commas still work as expected for non-subscripts. 31 | small_list = [ 32 | 1, 33 | ] 34 | list_of_types = [ 35 | tuple[int,], 36 | ] 37 | -------------------------------------------------------------------------------- /tests/data/cases/parenthesized_context_managers.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | with (CtxManager() as example): 3 | ... 4 | 5 | with (CtxManager1(), CtxManager2()): 6 | ... 7 | 8 | with (CtxManager1() as example, CtxManager2()): 9 | ... 10 | 11 | with (CtxManager1(), CtxManager2() as example): 12 | ... 13 | 14 | with (CtxManager1() as example1, CtxManager2() as example2): 15 | ... 16 | 17 | with ( 18 | CtxManager1() as example1, 19 | CtxManager2() as example2, 20 | CtxManager3() as example3, 21 | ): 22 | ... 23 | 24 | # output 25 | 26 | with CtxManager() as example: 27 | ... 28 | 29 | with CtxManager1(), CtxManager2(): 30 | ... 31 | 32 | with CtxManager1() as example, CtxManager2(): 33 | ... 34 | 35 | with CtxManager1(), CtxManager2() as example: 36 | ... 37 | 38 | with CtxManager1() as example1, CtxManager2() as example2: 39 | ... 40 | 41 | with ( 42 | CtxManager1() as example1, 43 | CtxManager2() as example2, 44 | CtxManager3() as example3, 45 | ): 46 | ... 47 | -------------------------------------------------------------------------------- /tests/data/cases/pattern_matching_extras.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | import match 3 | 4 | match something: 5 | case [a as b]: 6 | print(b) 7 | case [a as b, c, d, e as f]: 8 | print(f) 9 | case Point(a as b): 10 | print(b) 11 | case Point(int() as x, int() as y): 12 | print(x, y) 13 | 14 | 15 | match = 1 16 | case: int = re.match(something) 17 | 18 | match re.match(case): 19 | case type("match", match): 20 | pass 21 | case match: 22 | pass 23 | 24 | 25 | def func(match: case, case: match) -> case: 26 | match Something(): 27 | case func(match, case): 28 | ... 29 | case another: 30 | ... 31 | 32 | 33 | match a, *b, c: 34 | case [*_]: 35 | assert "seq" == _ 36 | case {}: 37 | assert "map" == b 38 | 39 | 40 | match match( 41 | case, 42 | match( 43 | match, case, match, looooooooooooooooooooooooooooooooooooong, match, case, match 44 | ), 45 | case, 46 | ): 47 | case case( 48 | match=case, 49 | case=re.match( 50 | loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong 51 | ), 52 | ): 53 | pass 54 | case [a as match]: 55 | pass 56 | case case: 57 | pass 58 | case something: 59 | pass 60 | 61 | 62 | match match: 63 | case case: 64 | pass 65 | 66 | 67 | match a, *b(), c: 68 | case d, *f, g: 69 | pass 70 | 71 | 72 | match something: 73 | case { 74 | "key": key as key_1, 75 | "password": PASS.ONE | PASS.TWO | PASS.THREE as password, 76 | }: 77 | pass 78 | case {"maybe": something(complicated as this) as that}: 79 | pass 80 | 81 | 82 | match something: 83 | case 1 as a: 84 | pass 85 | case 2 as b, 3 as c: 86 | pass 87 | case 4 as d, (5 as e), (6 | 7 as g), *h: 88 | pass 89 | 90 | 91 | match bar1: 92 | case Foo(aa=Callable() as aa, bb=int()): 93 | print(bar1.aa, bar1.bb) 94 | case _: 95 | print("no match", "\n") 96 | 97 | 98 | match bar1: 99 | case Foo( 100 | normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u 101 | ): 102 | pass 103 | -------------------------------------------------------------------------------- /tests/data/cases/pattern_matching_long.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | match x: 3 | case "abcd" | "abcd" | "abcd" : 4 | pass 5 | case "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd" | "abcd": 6 | pass 7 | case xxxxxxxxxxxxxxxxxxxxxxx: 8 | pass 9 | 10 | # output 11 | 12 | match x: 13 | case "abcd" | "abcd" | "abcd": 14 | pass 15 | case ( 16 | "abcd" 17 | | "abcd" 18 | | "abcd" 19 | | "abcd" 20 | | "abcd" 21 | | "abcd" 22 | | "abcd" 23 | | "abcd" 24 | | "abcd" 25 | | "abcd" 26 | | "abcd" 27 | | "abcd" 28 | | "abcd" 29 | | "abcd" 30 | | "abcd" 31 | ): 32 | pass 33 | case xxxxxxxxxxxxxxxxxxxxxxx: 34 | pass 35 | -------------------------------------------------------------------------------- /tests/data/cases/pattern_matching_style.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | match something: 3 | case b(): print(1+1) 4 | case c( 5 | very_complex=True, 6 | perhaps_even_loooooooooooooooooooooooooooooooooooooong=- 1 7 | ): print(1) 8 | case c( 9 | very_complex=True, 10 | perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1, 11 | ): print(2) 12 | case a: pass 13 | 14 | match( 15 | arg # comment 16 | ) 17 | 18 | match( 19 | ) 20 | 21 | match( 22 | 23 | 24 | ) 25 | 26 | case( 27 | arg # comment 28 | ) 29 | 30 | case( 31 | ) 32 | 33 | case( 34 | 35 | 36 | ) 37 | 38 | 39 | re.match( 40 | something # fast 41 | ) 42 | re.match( 43 | 44 | 45 | 46 | ) 47 | match match( 48 | 49 | 50 | ): 51 | case case( 52 | arg, # comment 53 | ): 54 | pass 55 | 56 | # output 57 | 58 | match something: 59 | case b(): 60 | print(1 + 1) 61 | case c( 62 | very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1 63 | ): 64 | print(1) 65 | case c( 66 | very_complex=True, 67 | perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1, 68 | ): 69 | print(2) 70 | case a: 71 | pass 72 | 73 | match(arg) # comment 74 | 75 | match() 76 | 77 | match() 78 | 79 | case(arg) # comment 80 | 81 | case() 82 | 83 | case() 84 | 85 | 86 | re.match(something) # fast 87 | re.match() 88 | match match(): 89 | case case( 90 | arg, # comment 91 | ): 92 | pass 93 | -------------------------------------------------------------------------------- /tests/data/cases/pattern_matching_trailing_comma.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | match maybe, multiple: 3 | case perhaps, 5: 4 | pass 5 | case perhaps, 6,: 6 | pass 7 | 8 | 9 | match more := (than, one), indeed,: 10 | case _, (5, 6): 11 | pass 12 | case [[5], (6)], [7],: 13 | pass 14 | case _: 15 | pass 16 | 17 | 18 | # output 19 | 20 | match maybe, multiple: 21 | case perhaps, 5: 22 | pass 23 | case ( 24 | perhaps, 25 | 6, 26 | ): 27 | pass 28 | 29 | 30 | match more := (than, one), indeed,: 31 | case _, (5, 6): 32 | pass 33 | case ( 34 | [[5], (6)], 35 | [7], 36 | ): 37 | pass 38 | case _: 39 | pass -------------------------------------------------------------------------------- /tests/data/cases/pep646_typed_star_arg_type_var_tuple.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.11 2 | 3 | 4 | def fn(*args: *tuple[*A, B]) -> None: 5 | pass 6 | 7 | 8 | fn.__annotations__ 9 | -------------------------------------------------------------------------------- /tests/data/cases/pep_570.py: -------------------------------------------------------------------------------- 1 | def positional_only_arg(a, /): 2 | pass 3 | 4 | 5 | def all_markers(a, b, /, c, d, *, e, f): 6 | pass 7 | 8 | 9 | def all_markers_with_args_and_kwargs( 10 | a_long_one, 11 | b_long_one, 12 | /, 13 | c_long_one, 14 | d_long_one, 15 | *args, 16 | e_long_one, 17 | f_long_one, 18 | **kwargs, 19 | ): 20 | pass 21 | 22 | 23 | def all_markers_with_defaults(a, b=1, /, c=2, d=3, *, e=4, f=5): 24 | pass 25 | 26 | 27 | def long_one_with_long_parameter_names( 28 | but_all_of_them, 29 | are_positional_only, 30 | arguments_mmmmkay, 31 | so_this_is_only_valid_after, 32 | three_point_eight, 33 | /, 34 | ): 35 | pass 36 | 37 | 38 | lambda a, /: a 39 | 40 | lambda a, b, /, c, d, *, e, f: a 41 | 42 | lambda a, b, /, c, d, *args, e, f, **kwargs: args 43 | 44 | lambda a, b=1, /, c=2, d=3, *, e=4, f=5: 1 45 | -------------------------------------------------------------------------------- /tests/data/cases/pep_572.py: -------------------------------------------------------------------------------- 1 | (a := 1) 2 | (a := a) 3 | if (match := pattern.search(data)) is None: 4 | pass 5 | if match := pattern.search(data): 6 | pass 7 | [y := f(x), y**2, y**3] 8 | filtered_data = [y for x in data if (y := f(x)) is None] 9 | (y := f(x)) 10 | y0 = (y1 := f(x)) 11 | foo(x=(y := f(x))) 12 | 13 | 14 | def foo(answer=(p := 42)): 15 | pass 16 | 17 | 18 | def foo(answer: (p := 42) = 5): 19 | pass 20 | 21 | 22 | lambda: (x := 1) 23 | (x := lambda: 1) 24 | (x := lambda: (y := 1)) 25 | lambda line: (m := re.match(pattern, line)) and m.group(1) 26 | x = (y := 0) 27 | (z := (y := (x := 0))) 28 | (info := (name, phone, *rest)) 29 | (x := 1, 2) 30 | (total := total + tax) 31 | len(lines := f.readlines()) 32 | foo(x := 3, cat="vector") 33 | foo(cat=(category := "vector")) 34 | if any(len(longline := l) >= 100 for l in lines): 35 | print(longline) 36 | if env_base := os.environ.get("PYTHONUSERBASE", None): 37 | return env_base 38 | if self._is_special and (ans := self._check_nans(context=context)): 39 | return ans 40 | foo(b := 2, a=1) 41 | foo((b := 2), a=1) 42 | foo(c=(b := 2), a=1) 43 | 44 | while x := f(x): 45 | pass 46 | while x := f(x): 47 | pass 48 | -------------------------------------------------------------------------------- /tests/data/cases/pep_572_do_not_remove_parens.py: -------------------------------------------------------------------------------- 1 | # flags: --fast 2 | # Most of the following examples are really dumb, some of them aren't even accepted by Python, 3 | # we're fixing them only so fuzzers (which follow the grammar which actually allows these 4 | # examples matter of fact!) don't yell at us :p 5 | 6 | del (a := [1]) 7 | 8 | try: 9 | pass 10 | except (a := 1) as (b := why_does_this_exist): 11 | pass 12 | 13 | for (z := 124) in (x := -124): 14 | pass 15 | 16 | with (y := [3, 2, 1]) as (funfunfun := indeed): 17 | pass 18 | 19 | 20 | @(please := stop) 21 | def sigh(): 22 | pass 23 | 24 | 25 | for (x := 3, y := 4) in y: 26 | pass 27 | -------------------------------------------------------------------------------- /tests/data/cases/pep_572_py310.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | # Unparenthesized walruses are now allowed in indices since Python 3.10. 3 | x[a := 0] 4 | x[a := 0, b := 1] 5 | x[5, b := 0] 6 | 7 | # Walruses are allowed inside generator expressions on function calls since 3.10. 8 | if any(match := pattern_error.match(s) for s in buffer): 9 | if match.group(2) == data_not_available: 10 | # Error OK to ignore. 11 | pass 12 | 13 | f(a := b + c for c in range(10)) 14 | f((a := b + c for c in range(10)), x) 15 | f(y=(a := b + c for c in range(10))) 16 | f(x, (a := b + c for c in range(10)), y=z, **q) 17 | 18 | 19 | # Don't remove parens when assignment expr is one of the exprs in a with statement 20 | with x, (a := b): 21 | pass 22 | -------------------------------------------------------------------------------- /tests/data/cases/pep_572_py39.py: -------------------------------------------------------------------------------- 1 | # Unparenthesized walruses are now allowed in set literals & set comprehensions 2 | # since Python 3.9 3 | {x := 1, 2, 3} 4 | {x4 := x**5 for x in range(7)} 5 | # We better not remove the parentheses here (since it's a 3.10 feature) 6 | x[(a := 1)] 7 | x[(a := 1), (b := 3)] 8 | -------------------------------------------------------------------------------- /tests/data/cases/pep_572_slices.py: -------------------------------------------------------------------------------- 1 | x[(a:=0):] 2 | x[:(a:=0)] 3 | 4 | # output 5 | x[(a := 0) :] 6 | x[: (a := 0)] 7 | -------------------------------------------------------------------------------- /tests/data/cases/pep_604.py: -------------------------------------------------------------------------------- 1 | def some_very_long_name_function() -> my_module.Asdf | my_module.AnotherType | my_module.YetAnotherType | None: 2 | pass 3 | 4 | 5 | def some_very_long_name_function() -> my_module.Asdf | my_module.AnotherType | my_module.YetAnotherType | my_module.EvenMoreType | None: 6 | pass 7 | 8 | 9 | # output 10 | 11 | 12 | def some_very_long_name_function() -> ( 13 | my_module.Asdf | my_module.AnotherType | my_module.YetAnotherType | None 14 | ): 15 | pass 16 | 17 | 18 | def some_very_long_name_function() -> ( 19 | my_module.Asdf 20 | | my_module.AnotherType 21 | | my_module.YetAnotherType 22 | | my_module.EvenMoreType 23 | | None 24 | ): 25 | pass 26 | -------------------------------------------------------------------------------- /tests/data/cases/pep_654.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.11 2 | try: 3 | raise OSError("blah") 4 | except* ExceptionGroup as e: 5 | pass 6 | 7 | 8 | try: 9 | async with trio.open_nursery() as nursery: 10 | # Make two concurrent calls to child() 11 | nursery.start_soon(child) 12 | nursery.start_soon(child) 13 | except* ValueError: 14 | pass 15 | 16 | try: 17 | try: 18 | raise ValueError(42) 19 | except: 20 | try: 21 | raise TypeError(int) 22 | except* Exception: 23 | pass 24 | 1 / 0 25 | except Exception as e: 26 | exc = e 27 | 28 | try: 29 | try: 30 | raise FalsyEG("eg", [TypeError(1), ValueError(2)]) 31 | except* TypeError as e: 32 | tes = e 33 | raise 34 | except* ValueError as e: 35 | ves = e 36 | pass 37 | except Exception as e: 38 | exc = e 39 | 40 | try: 41 | try: 42 | raise orig 43 | except* (TypeError, ValueError) as e: 44 | raise SyntaxError(3) from e 45 | except BaseException as e: 46 | exc = e 47 | 48 | try: 49 | try: 50 | raise orig 51 | except* OSError as e: 52 | raise TypeError(3) from e 53 | except ExceptionGroup as e: 54 | exc = e 55 | -------------------------------------------------------------------------------- /tests/data/cases/percent_precedence.py: -------------------------------------------------------------------------------- 1 | ("" % a) ** 2 2 | ("" % a)[0] 3 | ("" % a)() 4 | ("" % a).b 5 | 6 | 2 * ("" % a) 7 | 2 @ ("" % a) 8 | 2 / ("" % a) 9 | 2 // ("" % a) 10 | 2 % ("" % a) 11 | +("" % a) 12 | b + ("" % a) 13 | -("" % a) 14 | b - ("" % a) 15 | b + -("" % a) 16 | ~("" % a) 17 | 2 ** ("" % a) 18 | await ("" % a) 19 | b[("" % a)] 20 | b(("" % a)) 21 | # output 22 | ("" % a) ** 2 23 | ("" % a)[0] 24 | ("" % a)() 25 | ("" % a).b 26 | 27 | 2 * ("" % a) 28 | 2 @ ("" % a) 29 | 2 / ("" % a) 30 | 2 // ("" % a) 31 | 2 % ("" % a) 32 | +("" % a) 33 | b + ("" % a) 34 | -("" % a) 35 | b - ("" % a) 36 | b + -("" % a) 37 | ~("" % a) 38 | 2 ** ("" % a) 39 | await ("" % a) 40 | b[("" % a)] 41 | b(("" % a)) 42 | -------------------------------------------------------------------------------- /tests/data/cases/power_op_newline.py: -------------------------------------------------------------------------------- 1 | # flags: --line-length=0 2 | importA;()<<0**0# 3 | 4 | # output 5 | 6 | importA 7 | ( 8 | () 9 | << 0 10 | ** 0 11 | ) # 12 | -------------------------------------------------------------------------------- /tests/data/cases/prefer_rhs_split_reformatted.py: -------------------------------------------------------------------------------- 1 | # Test cases separate from `prefer_rhs_split.py` that contains unformatted source. 2 | 3 | # Left hand side fits in a single line but will still be exploded by the 4 | # magic trailing comma. 5 | first_value, (m1, m2,), third_value = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( 6 | arg1, 7 | arg2, 8 | ) 9 | 10 | # Make when when the left side of assignment plus the opening paren "... = (" is 11 | # exactly line length limit + 1, it won't be split like that. 12 | xxxxxxxxx_yyy_zzzzzzzz[xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1)] = 1 13 | 14 | # Regression test for #1187 15 | print( 16 | dict( 17 | a=1, 18 | b=2 if some_kind_of_data is not None else some_other_kind_of_data, # some explanation of why this is actually necessary 19 | c=3, 20 | ) 21 | ) 22 | 23 | # output 24 | 25 | 26 | # Test cases separate from `prefer_rhs_split.py` that contains unformatted source. 27 | 28 | # Left hand side fits in a single line but will still be exploded by the 29 | # magic trailing comma. 30 | ( 31 | first_value, 32 | ( 33 | m1, 34 | m2, 35 | ), 36 | third_value, 37 | ) = xxxxxx_yyyyyy_zzzzzz_wwwwww_uuuuuuu_vvvvvvvvvvv( 38 | arg1, 39 | arg2, 40 | ) 41 | 42 | # Make when when the left side of assignment plus the opening paren "... = (" is 43 | # exactly line length limit + 1, it won't be split like that. 44 | xxxxxxxxx_yyy_zzzzzzzz[ 45 | xx.xxxxxx(x_yyy_zzzzzz.xxxxx[0]), x_yyy_zzzzzz.xxxxxx(xxxx=1) 46 | ] = 1 47 | 48 | # Regression test for #1187 49 | print( 50 | dict( 51 | a=1, 52 | b=( 53 | 2 if some_kind_of_data is not None else some_other_kind_of_data 54 | ), # some explanation of why this is actually necessary 55 | c=3, 56 | ) 57 | ) 58 | -------------------------------------------------------------------------------- /tests/data/cases/preview_cantfit_string.py: -------------------------------------------------------------------------------- 1 | # flags: --unstable 2 | # long arguments 3 | normal_name = normal_function_name( 4 | "but with super long string arguments that on their own exceed the line limit so there's no way it can ever fit", 5 | "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", 6 | this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, 7 | ) 8 | 9 | # output 10 | 11 | # long arguments 12 | normal_name = normal_function_name( 13 | "but with super long string arguments that on their own exceed the line limit so" 14 | " there's no way it can ever fit", 15 | "eggs with spam and eggs and spam with eggs with spam and eggs and spam with eggs" 16 | " with spam and eggs and spam with eggs", 17 | this_is_a_ridiculously_long_name_and_nobody_in_their_right_mind_would_use_one_like_it=0, 18 | ) 19 | -------------------------------------------------------------------------------- /tests/data/cases/preview_fstring.py: -------------------------------------------------------------------------------- 1 | # flags: --unstable 2 | f"{''=}" f'{""=}' -------------------------------------------------------------------------------- /tests/data/cases/preview_long_strings__east_asian_width.py: -------------------------------------------------------------------------------- 1 | # flags: --unstable 2 | # The following strings do not have not-so-many chars, but are long enough 3 | # when these are rendered in a monospace font (if the renderer respects 4 | # Unicode East Asian Width properties). 5 | hangul = '코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이 필요한 문자열' 6 | hanzi = '中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長,因此需要換行的字符串。' 7 | japanese = 'コードポイントの数は少ないが、実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、改行が要る文字列' 8 | 9 | # output 10 | 11 | # The following strings do not have not-so-many chars, but are long enough 12 | # when these are rendered in a monospace font (if the renderer respects 13 | # Unicode East Asian Width properties). 14 | hangul = ( 15 | "코드포인트 수는 적으나 실제 터미널이나 에디터에서 렌더링될 땐 너무 길어서 줄바꿈이" 16 | " 필요한 문자열" 17 | ) 18 | hanzi = ( 19 | "中文測試:代碼點數量少,但在真正的終端模擬器或編輯器中呈現時太長," 20 | "因此需要換行的字符串。" 21 | ) 22 | japanese = ( 23 | "コードポイントの数は少ないが、" 24 | "実際の端末エミュレータやエディタでレンダリングされる時は長すぎる為、" 25 | "改行が要る文字列" 26 | ) 27 | -------------------------------------------------------------------------------- /tests/data/cases/preview_return_annotation_brackets_string.py: -------------------------------------------------------------------------------- 1 | # flags: --unstable 2 | # Long string example 3 | def frobnicate() -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": 4 | pass 5 | 6 | # splitting the string breaks if there's any parameters 7 | def frobnicate(a) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": 8 | pass 9 | 10 | # output 11 | 12 | # Long string example 13 | def frobnicate() -> ( 14 | "ThisIsTrulyUnreasonablyExtremelyLongClassName |" 15 | " list[ThisIsTrulyUnreasonablyExtremelyLongClassName]" 16 | ): 17 | pass 18 | 19 | 20 | # splitting the string breaks if there's any parameters 21 | def frobnicate( 22 | a, 23 | ) -> "ThisIsTrulyUnreasonablyExtremelyLongClassName | list[ThisIsTrulyUnreasonablyExtremelyLongClassName]": 24 | pass 25 | -------------------------------------------------------------------------------- /tests/data/cases/py310_pep572.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | x[a:=0] 3 | x[a := 0] 4 | x[a := 0, b := 1] 5 | x[5, b := 0] 6 | x[a:=0,b:=1] 7 | 8 | # output 9 | x[a := 0] 10 | x[a := 0] 11 | x[a := 0, b := 1] 12 | x[5, b := 0] 13 | x[a := 0, b := 1] 14 | -------------------------------------------------------------------------------- /tests/data/cases/python37.py: -------------------------------------------------------------------------------- 1 | def f(): 2 | return (i * 2 async for i in arange(42)) 3 | 4 | 5 | def g(): 6 | return ( 7 | something_long * something_long 8 | async for something_long in async_generator(with_an_argument) 9 | ) 10 | 11 | 12 | async def func(): 13 | if test: 14 | out_batched = [ 15 | i 16 | async for i in aitertools._async_map( 17 | self.async_inc, arange(8), batch_size=3 18 | ) 19 | ] 20 | 21 | 22 | def awaited_generator_value(n): 23 | return (await awaitable for awaitable in awaitable_list) 24 | 25 | 26 | def make_arange(n): 27 | return (i * 2 for i in range(n) if await wrap(i)) 28 | 29 | 30 | # output 31 | 32 | 33 | def f(): 34 | return (i * 2 async for i in arange(42)) 35 | 36 | 37 | def g(): 38 | return ( 39 | something_long * something_long 40 | async for something_long in async_generator(with_an_argument) 41 | ) 42 | 43 | 44 | async def func(): 45 | if test: 46 | out_batched = [ 47 | i 48 | async for i in aitertools._async_map( 49 | self.async_inc, arange(8), batch_size=3 50 | ) 51 | ] 52 | 53 | 54 | def awaited_generator_value(n): 55 | return (await awaitable for awaitable in awaitable_list) 56 | 57 | 58 | def make_arange(n): 59 | return (i * 2 for i in range(n) if await wrap(i)) 60 | -------------------------------------------------------------------------------- /tests/data/cases/python38.py: -------------------------------------------------------------------------------- 1 | def starred_return(): 2 | my_list = ["value2", "value3"] 3 | return "value1", *my_list 4 | 5 | 6 | def starred_yield(): 7 | my_list = ["value2", "value3"] 8 | yield "value1", *my_list 9 | 10 | 11 | # all right hand side expressions allowed in regular assignments are now also allowed in 12 | # annotated assignments 13 | a : Tuple[ str, int] = "1", 2 14 | a: Tuple[int , ... ] = b, *c, d 15 | def t(): 16 | a : str = yield "a" 17 | 18 | 19 | # output 20 | 21 | 22 | def starred_return(): 23 | my_list = ["value2", "value3"] 24 | return "value1", *my_list 25 | 26 | 27 | def starred_yield(): 28 | my_list = ["value2", "value3"] 29 | yield "value1", *my_list 30 | 31 | 32 | # all right hand side expressions allowed in regular assignments are now also allowed in 33 | # annotated assignments 34 | a: Tuple[str, int] = "1", 2 35 | a: Tuple[int, ...] = b, *c, d 36 | 37 | 38 | def t(): 39 | a: str = yield "a" 40 | -------------------------------------------------------------------------------- /tests/data/cases/python39.py: -------------------------------------------------------------------------------- 1 | @relaxed_decorator[0] 2 | def f(): 3 | ... 4 | 5 | @relaxed_decorator[extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length] 6 | def f(): 7 | ... 8 | 9 | @extremely_long_variable_name_that_doesnt_fit := complex.expression(with_long="arguments_value_that_wont_fit_at_the_end_of_the_line") 10 | def f(): 11 | ... 12 | 13 | # output 14 | 15 | @relaxed_decorator[0] 16 | def f(): ... 17 | 18 | 19 | @relaxed_decorator[ 20 | extremely_long_name_that_definitely_will_not_fit_on_one_line_of_standard_length 21 | ] 22 | def f(): ... 23 | 24 | 25 | @extremely_long_variable_name_that_doesnt_fit := complex.expression( 26 | with_long="arguments_value_that_wont_fit_at_the_end_of_the_line" 27 | ) 28 | def f(): ... -------------------------------------------------------------------------------- /tests/data/cases/raw_docstring.py: -------------------------------------------------------------------------------- 1 | # flags: --skip-string-normalization 2 | class C: 3 | 4 | r"""Raw""" 5 | 6 | def f(): 7 | 8 | r"""Raw""" 9 | 10 | class SingleQuotes: 11 | 12 | 13 | r'''Raw''' 14 | 15 | class UpperCaseR: 16 | R"""Raw""" 17 | 18 | # output 19 | class C: 20 | r"""Raw""" 21 | 22 | 23 | def f(): 24 | r"""Raw""" 25 | 26 | 27 | class SingleQuotes: 28 | r'''Raw''' 29 | 30 | 31 | class UpperCaseR: 32 | R"""Raw""" 33 | -------------------------------------------------------------------------------- /tests/data/cases/raw_docstring_no_string_normalization.py: -------------------------------------------------------------------------------- 1 | # flags: --skip-string-normalization 2 | def do_not_touch_this_prefix(): 3 | R"""There was a bug where docstring prefixes would be normalized even with -S.""" 4 | 5 | 6 | def do_not_touch_this_prefix2(): 7 | FR'There was a bug where docstring prefixes would be normalized even with -S.' 8 | 9 | 10 | def do_not_touch_this_prefix3(): 11 | u'''There was a bug where docstring prefixes would be normalized even with -S.''' 12 | -------------------------------------------------------------------------------- /tests/data/cases/remove_except_parens.py: -------------------------------------------------------------------------------- 1 | # These brackets are redundant, therefore remove. 2 | try: 3 | a.something 4 | except (AttributeError) as err: 5 | raise err 6 | 7 | # This is tuple of exceptions. 8 | # Although this could be replaced with just the exception, 9 | # we do not remove brackets to preserve AST. 10 | try: 11 | a.something 12 | except (AttributeError,) as err: 13 | raise err 14 | 15 | # This is a tuple of exceptions. Do not remove brackets. 16 | try: 17 | a.something 18 | except (AttributeError, ValueError) as err: 19 | raise err 20 | 21 | # Test long variants. 22 | try: 23 | a.something 24 | except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error) as err: 25 | raise err 26 | 27 | try: 28 | a.something 29 | except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error,) as err: 30 | raise err 31 | 32 | try: 33 | a.something 34 | except (some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error) as err: 35 | raise err 36 | 37 | # output 38 | # These brackets are redundant, therefore remove. 39 | try: 40 | a.something 41 | except AttributeError as err: 42 | raise err 43 | 44 | # This is tuple of exceptions. 45 | # Although this could be replaced with just the exception, 46 | # we do not remove brackets to preserve AST. 47 | try: 48 | a.something 49 | except (AttributeError,) as err: 50 | raise err 51 | 52 | # This is a tuple of exceptions. Do not remove brackets. 53 | try: 54 | a.something 55 | except (AttributeError, ValueError) as err: 56 | raise err 57 | 58 | # Test long variants. 59 | try: 60 | a.something 61 | except ( 62 | some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error 63 | ) as err: 64 | raise err 65 | 66 | try: 67 | a.something 68 | except ( 69 | some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, 70 | ) as err: 71 | raise err 72 | 73 | try: 74 | a.something 75 | except ( 76 | some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, 77 | some.really.really.really.looooooooooooooooooooooooooooooooong.module.over89.chars.Error, 78 | ) as err: 79 | raise err 80 | -------------------------------------------------------------------------------- /tests/data/cases/remove_for_brackets.py: -------------------------------------------------------------------------------- 1 | # Only remove tuple brackets after `for` 2 | for (k, v) in d.items(): 3 | print(k, v) 4 | 5 | # Don't touch tuple brackets after `in` 6 | for module in (core, _unicodefun): 7 | if hasattr(module, "_verify_python3_env"): 8 | module._verify_python3_env = lambda: None 9 | 10 | # Brackets remain for long for loop lines 11 | for (why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, i_dont_know_but_we_should_still_check_the_behaviour_if_they_do) in d.items(): 12 | print(k, v) 13 | 14 | for (k, v) in dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items(): 15 | print(k, v) 16 | 17 | # Test deeply nested brackets 18 | for (((((k, v))))) in d.items(): 19 | print(k, v) 20 | 21 | # output 22 | # Only remove tuple brackets after `for` 23 | for k, v in d.items(): 24 | print(k, v) 25 | 26 | # Don't touch tuple brackets after `in` 27 | for module in (core, _unicodefun): 28 | if hasattr(module, "_verify_python3_env"): 29 | module._verify_python3_env = lambda: None 30 | 31 | # Brackets remain for long for loop lines 32 | for ( 33 | why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, 34 | i_dont_know_but_we_should_still_check_the_behaviour_if_they_do, 35 | ) in d.items(): 36 | print(k, v) 37 | 38 | for ( 39 | k, 40 | v, 41 | ) in ( 42 | dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items() 43 | ): 44 | print(k, v) 45 | 46 | # Test deeply nested brackets 47 | for k, v in d.items(): 48 | print(k, v) 49 | -------------------------------------------------------------------------------- /tests/data/cases/remove_redundant_parens_in_case_guard.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 --line-length=79 2 | 3 | match 1: 4 | case _ if (True): 5 | pass 6 | 7 | 8 | match 1: 9 | case _ if ( 10 | True 11 | ): 12 | pass 13 | 14 | 15 | match 1: 16 | case _ if ( 17 | # this is a comment 18 | True 19 | ): 20 | pass 21 | 22 | 23 | match 1: 24 | case _ if ( 25 | True 26 | # this is a comment 27 | ): 28 | pass 29 | 30 | 31 | match 1: 32 | case _ if ( 33 | True # this is a comment 34 | ): 35 | pass 36 | 37 | 38 | match 1: 39 | case _ if ( # this is a comment 40 | True 41 | ): 42 | pass 43 | 44 | 45 | match 1: 46 | case _ if ( 47 | True 48 | ): # this is a comment 49 | pass 50 | 51 | 52 | match 1: 53 | case _ if (True): # comment over the line limit unless parens are removed x 54 | pass 55 | 56 | 57 | match 1: 58 | case _ if (True): # comment over the line limit and parens should go to next line 59 | pass 60 | 61 | 62 | # output 63 | 64 | match 1: 65 | case _ if True: 66 | pass 67 | 68 | 69 | match 1: 70 | case _ if True: 71 | pass 72 | 73 | 74 | match 1: 75 | case _ if ( 76 | # this is a comment 77 | True 78 | ): 79 | pass 80 | 81 | 82 | match 1: 83 | case _ if ( 84 | True 85 | # this is a comment 86 | ): 87 | pass 88 | 89 | 90 | match 1: 91 | case _ if True: # this is a comment 92 | pass 93 | 94 | 95 | match 1: 96 | case _ if True: # this is a comment 97 | pass 98 | 99 | 100 | match 1: 101 | case _ if True: # this is a comment 102 | pass 103 | 104 | 105 | match 1: 106 | case _ if True: # comment over the line limit unless parens are removed x 107 | pass 108 | 109 | 110 | match 1: 111 | case ( 112 | _ 113 | ) if True: # comment over the line limit and parens should go to next line 114 | pass 115 | -------------------------------------------------------------------------------- /tests/data/cases/single_line_format_skip_with_multiple_comments.py: -------------------------------------------------------------------------------- 1 | foo = 123 # fmt: skip # noqa: E501 # pylint 2 | bar = ( 3 | 123 , 4 | ( 1 + 5 ) # pylint # fmt:skip 5 | ) 6 | baz = "a" + "b" # pylint; fmt: skip; noqa: E501 7 | skip_will_not_work = "a" + "b" # pylint fmt:skip 8 | skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it 9 | 10 | # output 11 | 12 | foo = 123 # fmt: skip # noqa: E501 # pylint 13 | bar = ( 14 | 123 , 15 | ( 1 + 5 ) # pylint # fmt:skip 16 | ) 17 | baz = "a" + "b" # pylint; fmt: skip; noqa: E501 18 | skip_will_not_work = "a" + "b" # pylint fmt:skip 19 | skip_will_not_work2 = "a" + "b" # some text; fmt:skip happens to be part of it 20 | -------------------------------------------------------------------------------- /tests/data/cases/skip_magic_trailing_comma.py: -------------------------------------------------------------------------------- 1 | # flags: --skip-magic-trailing-comma 2 | # We should not remove the trailing comma in a single-element subscript. 3 | a: tuple[int,] 4 | b = tuple[int,] 5 | 6 | # But commas in multiple element subscripts should be removed. 7 | c: tuple[int, int,] 8 | d = tuple[int, int,] 9 | 10 | # Remove commas for non-subscripts. 11 | small_list = [1,] 12 | list_of_types = [tuple[int,],] 13 | small_set = {1,} 14 | set_of_types = {tuple[int,],} 15 | 16 | # Except single element tuples 17 | small_tuple = (1,) 18 | 19 | # Trailing commas in multiple chained non-nested parens. 20 | zero( 21 | one, 22 | ).two( 23 | three, 24 | ).four( 25 | five, 26 | ) 27 | 28 | func1(arg1).func2(arg2,).func3(arg3).func4(arg4,).func5(arg5) 29 | 30 | ( 31 | a, 32 | b, 33 | c, 34 | d, 35 | ) = func1( 36 | arg1 37 | ) and func2(arg2) 38 | 39 | func( 40 | argument1, 41 | ( 42 | one, 43 | two, 44 | ), 45 | argument4, 46 | argument5, 47 | argument6, 48 | ) 49 | 50 | # output 51 | # We should not remove the trailing comma in a single-element subscript. 52 | a: tuple[int,] 53 | b = tuple[int,] 54 | 55 | # But commas in multiple element subscripts should be removed. 56 | c: tuple[int, int] 57 | d = tuple[int, int] 58 | 59 | # Remove commas for non-subscripts. 60 | small_list = [1] 61 | list_of_types = [tuple[int,]] 62 | small_set = {1} 63 | set_of_types = {tuple[int,]} 64 | 65 | # Except single element tuples 66 | small_tuple = (1,) 67 | 68 | # Trailing commas in multiple chained non-nested parens. 69 | zero(one).two(three).four(five) 70 | 71 | func1(arg1).func2(arg2).func3(arg3).func4(arg4).func5(arg5) 72 | 73 | (a, b, c, d) = func1(arg1) and func2(arg2) 74 | 75 | func(argument1, (one, two), argument4, argument5, argument6) 76 | -------------------------------------------------------------------------------- /tests/data/cases/slices.py: -------------------------------------------------------------------------------- 1 | slice[a.b : c.d] 2 | slice[d :: d + 1] 3 | slice[d + 1 :: d] 4 | slice[d::d] 5 | slice[0] 6 | slice[-1] 7 | slice[:-1] 8 | slice[::-1] 9 | slice[:c, c - 1] 10 | slice[c, c + 1, d::] 11 | slice[ham[c::d] :: 1] 12 | slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] 13 | slice[:-1:] 14 | slice[lambda: None : lambda: None] 15 | slice[lambda x, y, *args, really=2, **kwargs: None :, None::] 16 | slice[1 or 2 : True and False] 17 | slice[not so_simple : 1 < val <= 10] 18 | slice[(1 for i in range(42)) : x] 19 | slice[:: [i for i in range(42)]] 20 | 21 | 22 | async def f(): 23 | slice[await x : [i async for i in arange(42)] : 42] 24 | 25 | 26 | # These are from PEP-8: 27 | ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] 28 | ham[lower:upper], ham[lower:upper:], ham[lower::step] 29 | # ham[lower+offset : upper+offset] 30 | ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] 31 | ham[lower + offset : upper + offset] 32 | -------------------------------------------------------------------------------- /tests/data/cases/split_delimiter_comments.py: -------------------------------------------------------------------------------- 1 | a = ( 2 | 1 + # type: ignore 3 | 2 # type: ignore 4 | ) 5 | a = ( 6 | 1 # type: ignore 7 | + 2 # type: ignore 8 | ) 9 | bad_split3 = ( 10 | "What if we have inline comments on " # First Comment 11 | "each line of a bad split? In that " # Second Comment 12 | "case, we should just leave it alone." # Third Comment 13 | ) 14 | parametrize( 15 | ( 16 | {}, 17 | {}, 18 | ), 19 | ( # foobar 20 | {}, 21 | {}, 22 | ), 23 | ) 24 | 25 | 26 | 27 | # output 28 | a = ( 29 | 1 # type: ignore 30 | + 2 # type: ignore 31 | ) 32 | a = ( 33 | 1 # type: ignore 34 | + 2 # type: ignore 35 | ) 36 | bad_split3 = ( 37 | "What if we have inline comments on " # First Comment 38 | "each line of a bad split? In that " # Second Comment 39 | "case, we should just leave it alone." # Third Comment 40 | ) 41 | parametrize( 42 | ( 43 | {}, 44 | {}, 45 | ), 46 | ( # foobar 47 | {}, 48 | {}, 49 | ), 50 | ) 51 | 52 | -------------------------------------------------------------------------------- /tests/data/cases/starred_for_target.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | for x in *a, *b: 3 | print(x) 4 | 5 | for x in a, b, *c: 6 | print(x) 7 | 8 | for x in *a, b, c: 9 | print(x) 10 | 11 | for x in *a, b, *c: 12 | print(x) 13 | 14 | async for x in *a, *b: 15 | print(x) 16 | 17 | async for x in *a, b, *c: 18 | print(x) 19 | 20 | async for x in a, b, *c: 21 | print(x) 22 | 23 | async for x in ( 24 | *loooooooooooooooooooooong, 25 | very, 26 | *loooooooooooooooooooooooooooooooooooooooooooooooong, 27 | ): 28 | print(x) 29 | -------------------------------------------------------------------------------- /tests/data/cases/string_prefixes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | name = "Łukasz" 4 | (f"hello {name}", F"hello {name}") 5 | (b"", B"") 6 | (u"", U"") 7 | (r"", R"") 8 | 9 | (rf"", fr"", Rf"", fR"", rF"", Fr"", RF"", FR"") 10 | (rb"", br"", Rb"", bR"", rB"", Br"", RB"", BR"") 11 | 12 | 13 | def docstring_singleline(): 14 | R"""2020 was one hell of a year. The good news is that we were able to""" 15 | 16 | 17 | def docstring_multiline(): 18 | R""" 19 | clear out all of the issues opened in that time :p 20 | """ 21 | 22 | 23 | # output 24 | 25 | 26 | #!/usr/bin/env python3 27 | 28 | name = "Łukasz" 29 | (f"hello {name}", f"hello {name}") 30 | (b"", b"") 31 | ("", "") 32 | (r"", R"") 33 | 34 | (rf"", rf"", Rf"", Rf"", rf"", rf"", Rf"", Rf"") 35 | (rb"", rb"", Rb"", Rb"", rb"", rb"", Rb"", Rb"") 36 | 37 | 38 | def docstring_singleline(): 39 | R"""2020 was one hell of a year. The good news is that we were able to""" 40 | 41 | 42 | def docstring_multiline(): 43 | R""" 44 | clear out all of the issues opened in that time :p 45 | """ 46 | -------------------------------------------------------------------------------- /tests/data/cases/trailing_comma.py: -------------------------------------------------------------------------------- 1 | e = { 2 | "a": fun(msg, "ts"), 3 | "longggggggggggggggid": ..., 4 | "longgggggggggggggggggggkey": ..., "created": ... 5 | # "longkey": ... 6 | } 7 | f = [ 8 | arg1, 9 | arg2, 10 | arg3, arg4 11 | # comment 12 | ] 13 | g = ( 14 | arg1, 15 | arg2, 16 | arg3, arg4 17 | # comment 18 | ) 19 | h = { 20 | arg1, 21 | arg2, 22 | arg3, arg4 23 | # comment 24 | } 25 | 26 | # output 27 | 28 | e = { 29 | "a": fun(msg, "ts"), 30 | "longggggggggggggggid": ..., 31 | "longgggggggggggggggggggkey": ..., 32 | "created": ..., 33 | # "longkey": ... 34 | } 35 | f = [ 36 | arg1, 37 | arg2, 38 | arg3, 39 | arg4, 40 | # comment 41 | ] 42 | g = ( 43 | arg1, 44 | arg2, 45 | arg3, 46 | arg4, 47 | # comment 48 | ) 49 | h = { 50 | arg1, 51 | arg2, 52 | arg3, 53 | arg4, 54 | # comment 55 | } 56 | -------------------------------------------------------------------------------- /tests/data/cases/trailing_comma_optional_parens1.py: -------------------------------------------------------------------------------- 1 | if e1234123412341234.winerror not in (_winapi.ERROR_SEM_TIMEOUT, 2 | _winapi.ERROR_PIPE_BUSY) or _check_timeout(t): 3 | pass 4 | 5 | if x: 6 | if y: 7 | new_id = max(Vegetable.objects.order_by('-id')[0].id, 8 | Mineral.objects.order_by('-id')[0].id) + 1 9 | 10 | class X: 11 | def get_help_text(self): 12 | return ngettext( 13 | "Your password must contain at least %(min_length)d character.", 14 | "Your password must contain at least %(min_length)d characters.", 15 | self.min_length, 16 | ) % {'min_length': self.min_length} 17 | 18 | class A: 19 | def b(self): 20 | if self.connection.mysql_is_mariadb and ( 21 | 10, 22 | 4, 23 | 3, 24 | ) < self.connection.mysql_version < (10, 5, 2): 25 | pass 26 | 27 | 28 | # output 29 | 30 | if e1234123412341234.winerror not in ( 31 | _winapi.ERROR_SEM_TIMEOUT, 32 | _winapi.ERROR_PIPE_BUSY, 33 | ) or _check_timeout(t): 34 | pass 35 | 36 | if x: 37 | if y: 38 | new_id = ( 39 | max( 40 | Vegetable.objects.order_by("-id")[0].id, 41 | Mineral.objects.order_by("-id")[0].id, 42 | ) 43 | + 1 44 | ) 45 | 46 | 47 | class X: 48 | def get_help_text(self): 49 | return ngettext( 50 | "Your password must contain at least %(min_length)d character.", 51 | "Your password must contain at least %(min_length)d characters.", 52 | self.min_length, 53 | ) % {"min_length": self.min_length} 54 | 55 | 56 | class A: 57 | def b(self): 58 | if self.connection.mysql_is_mariadb and ( 59 | 10, 60 | 4, 61 | 3, 62 | ) < self.connection.mysql_version < (10, 5, 2): 63 | pass 64 | -------------------------------------------------------------------------------- /tests/data/cases/trailing_comma_optional_parens2.py: -------------------------------------------------------------------------------- 1 | if (e123456.get_tk_patchlevel() >= (8, 6, 0, 'final') or 2 | (8, 5, 8) <= get_tk_patchlevel() < (8, 6)): 3 | pass 4 | 5 | # output 6 | 7 | if e123456.get_tk_patchlevel() >= (8, 6, 0, "final") or ( 8 | 8, 9 | 5, 10 | 8, 11 | ) <= get_tk_patchlevel() < (8, 6): 12 | pass 13 | -------------------------------------------------------------------------------- /tests/data/cases/trailing_comma_optional_parens3.py: -------------------------------------------------------------------------------- 1 | if True: 2 | if True: 3 | if True: 4 | return _( 5 | "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas " 6 | + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.", 7 | "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe", 8 | ) % {"reported_username": reported_username, "report_reason": report_reason} 9 | 10 | 11 | # output 12 | 13 | 14 | if True: 15 | if True: 16 | if True: 17 | return _( 18 | "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweas " 19 | + "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwegqweasdzxcqweasdzxc.", 20 | "qweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqweasdzxcqwe", 21 | ) % {"reported_username": reported_username, "report_reason": report_reason} 22 | -------------------------------------------------------------------------------- /tests/data/cases/tricky_unicode_symbols.py: -------------------------------------------------------------------------------- 1 | ä = 1 2 | µ = 2 3 | 蟒 = 3 4 | x󠄀 = 4 5 | មុ = 1 6 | Q̇_per_meter = 4 7 | 8 | A᧚ = 3 9 | A፩ = 8 10 | -------------------------------------------------------------------------------- /tests/data/cases/tupleassign.py: -------------------------------------------------------------------------------- 1 | # This is a standalone comment. 2 | sdfjklsdfsjldkflkjsf, sdfjsdfjlksdljkfsdlkf, sdfsdjfklsdfjlksdljkf, sdsfsdfjskdflsfsdf = 1, 2, 3 3 | 4 | # This is as well. 5 | this_will_be_wrapped_in_parens, = struct.unpack(b"12345678901234567890") 6 | 7 | (a,) = call() 8 | 9 | # output 10 | 11 | 12 | # This is a standalone comment. 13 | ( 14 | sdfjklsdfsjldkflkjsf, 15 | sdfjsdfjlksdljkfsdlkf, 16 | sdfsdjfklsdfjlksdljkf, 17 | sdsfsdfjskdflsfsdf, 18 | ) = (1, 2, 3) 19 | 20 | # This is as well. 21 | (this_will_be_wrapped_in_parens,) = struct.unpack(b"12345678901234567890") 22 | 23 | (a,) = call() -------------------------------------------------------------------------------- /tests/data/cases/type_aliases.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.12 2 | 3 | type A=int 4 | type Gen[T]=list[T] 5 | type Alias[T]=lambda: T 6 | type And[T]=T and T 7 | type IfElse[T]=T if T else T 8 | type One = int; type Another = str 9 | class X: type InClass = int 10 | 11 | type = aliased 12 | print(type(42)) 13 | 14 | # output 15 | 16 | type A = int 17 | type Gen[T] = list[T] 18 | type Alias[T] = lambda: T 19 | type And[T] = T and T 20 | type IfElse[T] = T if T else T 21 | type One = int 22 | type Another = str 23 | 24 | 25 | class X: 26 | type InClass = int 27 | 28 | 29 | type = aliased 30 | print(type(42)) 31 | -------------------------------------------------------------------------------- /tests/data/cases/type_comment_syntax_error.py: -------------------------------------------------------------------------------- 1 | def foo( 2 | # type: Foo 3 | x): pass 4 | 5 | # output 6 | 7 | def foo( 8 | # type: Foo 9 | x, 10 | ): 11 | pass 12 | -------------------------------------------------------------------------------- /tests/data/cases/type_param_defaults.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.13 2 | 3 | type A[T=int] = float 4 | type B[*P=int] = float 5 | type C[*Ts=int] = float 6 | type D[*Ts=*int] = float 7 | type D[something_that_is_very_very_very_long=something_that_is_very_very_very_long] = float 8 | type D[*something_that_is_very_very_very_long=*something_that_is_very_very_very_long] = float 9 | type something_that_is_long[something_that_is_long=something_that_is_long] = something_that_is_long 10 | 11 | def simple[T=something_that_is_long](short1: int, short2: str, short3: bytes) -> float: 12 | pass 13 | 14 | def longer[something_that_is_long=something_that_is_long](something_that_is_long: something_that_is_long) -> something_that_is_long: 15 | pass 16 | 17 | def trailing_comma1[T=int,](a: str): 18 | pass 19 | 20 | def trailing_comma2[T=int](a: str,): 21 | pass 22 | 23 | def weird_syntax[T=lambda: 42, **P=lambda: 43, *Ts=lambda: 44](): pass 24 | 25 | # output 26 | 27 | type A[T = int] = float 28 | type B[*P = int] = float 29 | type C[*Ts = int] = float 30 | type D[*Ts = *int] = float 31 | type D[ 32 | something_that_is_very_very_very_long = something_that_is_very_very_very_long 33 | ] = float 34 | type D[ 35 | *something_that_is_very_very_very_long = *something_that_is_very_very_very_long 36 | ] = float 37 | type something_that_is_long[ 38 | something_that_is_long = something_that_is_long 39 | ] = something_that_is_long 40 | 41 | 42 | def simple[T = something_that_is_long]( 43 | short1: int, short2: str, short3: bytes 44 | ) -> float: 45 | pass 46 | 47 | 48 | def longer[something_that_is_long = something_that_is_long]( 49 | something_that_is_long: something_that_is_long, 50 | ) -> something_that_is_long: 51 | pass 52 | 53 | 54 | def trailing_comma1[ 55 | T = int, 56 | ]( 57 | a: str, 58 | ): 59 | pass 60 | 61 | 62 | def trailing_comma2[T = int]( 63 | a: str, 64 | ): 65 | pass 66 | 67 | 68 | def weird_syntax[T = lambda: 42, **P = lambda: 43, *Ts = lambda: 44](): 69 | pass 70 | -------------------------------------------------------------------------------- /tests/data/cases/type_params.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.12 2 | def func [T ](): pass 3 | async def func [ T ] (): pass 4 | class C[ T ] : pass 5 | 6 | def all_in[T : int,U : (bytes, str),* Ts,**P](): pass 7 | 8 | def really_long[WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitThisLine](): pass 9 | 10 | def even_longer[WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitThisLine: WhatIfItHadABound](): pass 11 | 12 | def it_gets_worse[WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitThisLine, ItCouldBeGenericOverMultipleTypeVars](): pass 13 | 14 | def magic[Trailing, Comma,](): pass 15 | 16 | def weird_syntax[T: lambda: 42, U: a or b](): pass 17 | 18 | # output 19 | 20 | 21 | def func[T](): 22 | pass 23 | 24 | 25 | async def func[T](): 26 | pass 27 | 28 | 29 | class C[T]: 30 | pass 31 | 32 | 33 | def all_in[T: int, U: (bytes, str), *Ts, **P](): 34 | pass 35 | 36 | 37 | def really_long[ 38 | WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitThisLine 39 | ](): 40 | pass 41 | 42 | 43 | def even_longer[ 44 | WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitThisLine: WhatIfItHadABound 45 | ](): 46 | pass 47 | 48 | 49 | def it_gets_worse[ 50 | WhatIsTheLongestTypeVarNameYouCanThinkOfEnoughToMakeBlackSplitThisLine, 51 | ItCouldBeGenericOverMultipleTypeVars, 52 | ](): 53 | pass 54 | 55 | 56 | def magic[ 57 | Trailing, 58 | Comma, 59 | ](): 60 | pass 61 | 62 | 63 | def weird_syntax[T: lambda: 42, U: a or b](): 64 | pass 65 | -------------------------------------------------------------------------------- /tests/data/cases/typed_params_trailing_comma.py: -------------------------------------------------------------------------------- 1 | def long_function_name_goes_here( 2 | x: Callable[List[int]] 3 | ) -> Union[List[int], float, str, bytes, Tuple[int]]: 4 | pass 5 | 6 | 7 | def long_function_name_goes_here( 8 | x: Callable[[str, Any], int] 9 | ) -> Union[List[int], float, str, bytes, Tuple[int]]: 10 | pass 11 | 12 | 13 | # output 14 | def long_function_name_goes_here( 15 | x: Callable[List[int]], 16 | ) -> Union[List[int], float, str, bytes, Tuple[int]]: 17 | pass 18 | 19 | 20 | def long_function_name_goes_here( 21 | x: Callable[[str, Any], int], 22 | ) -> Union[List[int], float, str, bytes, Tuple[int]]: 23 | pass 24 | -------------------------------------------------------------------------------- /tests/data/cases/walrus_in_dict.py: -------------------------------------------------------------------------------- 1 | # flags: --preview 2 | # This is testing an issue that is specific to the preview style (wrap_long_dict_values_in_parens) 3 | { 4 | "is_update": (up := commit.hash in update_hashes) 5 | } 6 | 7 | # output 8 | # This is testing an issue that is specific to the preview style (wrap_long_dict_values_in_parens) 9 | {"is_update": (up := commit.hash in update_hashes)} 10 | -------------------------------------------------------------------------------- /tests/data/cases/whitespace.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # output 7 | -------------------------------------------------------------------------------- /tests/data/empty_pyproject.toml: -------------------------------------------------------------------------------- 1 | # Empty pyproject.toml to use with some tests that depend on Python 3.6 autodiscovery 2 | # and so on. 3 | -------------------------------------------------------------------------------- /tests/data/gitignore_used_on_multiple_sources/.gitignore: -------------------------------------------------------------------------------- 1 | a.py 2 | -------------------------------------------------------------------------------- /tests/data/gitignore_used_on_multiple_sources/dir1/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/gitignore_used_on_multiple_sources/dir1/a.py -------------------------------------------------------------------------------- /tests/data/gitignore_used_on_multiple_sources/dir1/b.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/gitignore_used_on_multiple_sources/dir1/b.py -------------------------------------------------------------------------------- /tests/data/gitignore_used_on_multiple_sources/dir2/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/gitignore_used_on_multiple_sources/dir2/a.py -------------------------------------------------------------------------------- /tests/data/gitignore_used_on_multiple_sources/dir2/b.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/gitignore_used_on_multiple_sources/dir2/b.py -------------------------------------------------------------------------------- /tests/data/ignore_directory_gitignore_tests/.gitignore: -------------------------------------------------------------------------------- 1 | large_ignored_dir/ 2 | large_ignored_dir_two 3 | abc.py 4 | -------------------------------------------------------------------------------- /tests/data/ignore_directory_gitignore_tests/abc.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_directory_gitignore_tests/abc.py -------------------------------------------------------------------------------- /tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/a.py -------------------------------------------------------------------------------- /tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/inner/b.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/inner/b.py -------------------------------------------------------------------------------- /tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/inner2/c.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/inner2/c.py -------------------------------------------------------------------------------- /tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/inner3/d.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_directory_gitignore_tests/large_ignored_dir_two/inner3/d.py -------------------------------------------------------------------------------- /tests/data/ignore_directory_gitignore_tests/z.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_directory_gitignore_tests/z.py -------------------------------------------------------------------------------- /tests/data/ignore_subfolders_gitignore_tests/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_subfolders_gitignore_tests/a.py -------------------------------------------------------------------------------- /tests/data/ignore_subfolders_gitignore_tests/subdir/.gitignore: -------------------------------------------------------------------------------- 1 | */* 2 | -------------------------------------------------------------------------------- /tests/data/ignore_subfolders_gitignore_tests/subdir/b.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_subfolders_gitignore_tests/subdir/b.py -------------------------------------------------------------------------------- /tests/data/ignore_subfolders_gitignore_tests/subdir/subdir/c.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/ignore_subfolders_gitignore_tests/subdir/subdir/c.py -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/.gitignore: -------------------------------------------------------------------------------- 1 | dont_exclude/ 2 | -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/.definitely_exclude/a.pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/.definitely_exclude/a.pie -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/.definitely_exclude/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/.definitely_exclude/a.py -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/.definitely_exclude/a.pyi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/.definitely_exclude/a.pyi -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/dont_exclude/a.pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/dont_exclude/a.pie -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/dont_exclude/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/dont_exclude/a.py -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/dont_exclude/a.pyi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/dont_exclude/a.pyi -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/exclude/a.pie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/exclude/a.pie -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/exclude/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/exclude/a.py -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/b/exclude/a.pyi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/include_exclude_tests/b/exclude/a.pyi -------------------------------------------------------------------------------- /tests/data/include_exclude_tests/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=41.0", "setuptools-scm", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /tests/data/incorrect_spelling.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | ine_length = 50 3 | target-ersion = ['py37'] 4 | exclude='\.pyi?$' 5 | include='\.py?$' -------------------------------------------------------------------------------- /tests/data/invalid_gitignore_tests/.gitignore: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /tests/data/invalid_gitignore_tests/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/invalid_gitignore_tests/a.py -------------------------------------------------------------------------------- /tests/data/invalid_gitignore_tests/pyproject.toml: -------------------------------------------------------------------------------- 1 | # Empty configuration file; used in tests to avoid interference from Black's own config. 2 | -------------------------------------------------------------------------------- /tests/data/invalid_line_ranges.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-ranges = "1-1" 3 | -------------------------------------------------------------------------------- /tests/data/invalid_nested_gitignore_tests/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/invalid_nested_gitignore_tests/a.py -------------------------------------------------------------------------------- /tests/data/invalid_nested_gitignore_tests/a/.gitignore: -------------------------------------------------------------------------------- 1 | ! 2 | -------------------------------------------------------------------------------- /tests/data/invalid_nested_gitignore_tests/a/a.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/invalid_nested_gitignore_tests/a/a.py -------------------------------------------------------------------------------- /tests/data/invalid_nested_gitignore_tests/pyproject.toml: -------------------------------------------------------------------------------- 1 | # Empty configuration file; used in tests to avoid interference from Black's own config. 2 | -------------------------------------------------------------------------------- /tests/data/jupyter/non_python_notebook.ipynb: -------------------------------------------------------------------------------- 1 | {"metadata":{"kernelspec":{"name":"ir","display_name":"R","language":"R"},"language_info":{"name":"R","codemirror_mode":"r","pygments_lexer":"r","mimetype":"text/x-r-source","file_extension":".r","version":"4.0.5"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"library(tidyverse) ","metadata":{"_uuid":"051d70d956493feee0c6d64651c6a088724dca2a","_execution_state":"idle"},"execution_count":null,"outputs":[]}]} -------------------------------------------------------------------------------- /tests/data/jupyter/notebook_empty_metadata.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "tags": [] 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%%time\n", 12 | "\n", 13 | "print('foo')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [] 22 | } 23 | ], 24 | "metadata": {}, 25 | "nbformat": 4, 26 | "nbformat_minor": 4 27 | } 28 | -------------------------------------------------------------------------------- /tests/data/jupyter/notebook_no_trailing_newline.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "tags": [] 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%%time\n", 12 | "\n", 13 | "print('foo')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [] 22 | } 23 | ], 24 | "metadata": { 25 | "interpreter": { 26 | "hash": "e758f3098b5b55f4d87fe30bbdc1367f20f246b483f96267ee70e6c40cb185d8" 27 | }, 28 | "kernelspec": { 29 | "display_name": "Python 3.8.10 64-bit ('black': venv)", 30 | "name": "python3" 31 | }, 32 | "language_info": { 33 | "name": "python", 34 | "version": "" 35 | } 36 | }, 37 | "nbformat": 4, 38 | "nbformat_minor": 4 39 | } -------------------------------------------------------------------------------- /tests/data/jupyter/notebook_trailing_newline.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "tags": [] 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%%time\n", 12 | "\n", 13 | "print('foo')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": null, 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [] 22 | } 23 | ], 24 | "metadata": { 25 | "interpreter": { 26 | "hash": "e758f3098b5b55f4d87fe30bbdc1367f20f246b483f96267ee70e6c40cb185d8" 27 | }, 28 | "kernelspec": { 29 | "display_name": "Python 3.8.10 64-bit ('black': venv)", 30 | "name": "python3" 31 | }, 32 | "language_info": { 33 | "name": "python", 34 | "version": "" 35 | } 36 | }, 37 | "nbformat": 4, 38 | "nbformat_minor": 4 39 | } 40 | -------------------------------------------------------------------------------- /tests/data/jupyter/notebook_which_cant_be_parsed.ipynb: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /tests/data/jupyter/notebook_without_changes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "tags": [] 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%%time\n", 12 | "\n", 13 | "print(\"foo\")" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "This notebook should not be reformatted" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [] 29 | } 30 | ], 31 | "metadata": { 32 | "interpreter": { 33 | "hash": "e758f3098b5b55f4d87fe30bbdc1367f20f246b483f96267ee70e6c40cb185d8" 34 | }, 35 | "kernelspec": { 36 | "display_name": "Python 3.8.10 64-bit ('black': venv)", 37 | "name": "python3" 38 | }, 39 | "language_info": { 40 | "name": "python", 41 | "version": "" 42 | } 43 | }, 44 | "nbformat": 4, 45 | "nbformat_minor": 4 46 | } -------------------------------------------------------------------------------- /tests/data/line_ranges_formatted/basic.py: -------------------------------------------------------------------------------- 1 | """Module doc.""" 2 | 3 | from typing import ( 4 | Callable, 5 | Literal, 6 | ) 7 | 8 | 9 | # fmt: off 10 | class Unformatted: 11 | def should_also_work(self): 12 | pass 13 | # fmt: on 14 | 15 | 16 | a = [1, 2] # fmt: skip 17 | 18 | 19 | # This should cover as many syntaxes as possible. 20 | class Foo: 21 | """Class doc.""" 22 | 23 | def __init__(self) -> None: 24 | pass 25 | 26 | @add_logging 27 | @memoize.memoize(max_items=2) 28 | def plus_one( 29 | self, 30 | number: int, 31 | ) -> int: 32 | return number + 1 33 | 34 | async def async_plus_one(self, number: int) -> int: 35 | await asyncio.sleep(1) 36 | async with some_context(): 37 | return number + 1 38 | 39 | 40 | try: 41 | for i in range(10): 42 | while condition: 43 | if something: 44 | then_something() 45 | elif something_else: 46 | then_something_else() 47 | except ValueError as e: 48 | handle(e) 49 | finally: 50 | done() 51 | -------------------------------------------------------------------------------- /tests/data/line_ranges_formatted/pattern_matching.py: -------------------------------------------------------------------------------- 1 | # flags: --minimum-version=3.10 2 | 3 | 4 | def pattern_matching(): 5 | match status: 6 | case 1: 7 | return "1" 8 | case [single]: 9 | return "single" 10 | case [ 11 | action, 12 | obj, 13 | ]: 14 | return "act on obj" 15 | case Point(x=0): 16 | return "class pattern" 17 | case {"text": message}: 18 | return "mapping" 19 | case { 20 | "text": message, 21 | "format": _, 22 | }: 23 | return "mapping" 24 | case _: 25 | return "fallback" 26 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/async_as_identifier.py: -------------------------------------------------------------------------------- 1 | def async(): 2 | pass 3 | 4 | 5 | def await(): 6 | pass 7 | 8 | 9 | await = lambda: None 10 | async = lambda: None 11 | async() 12 | await() 13 | 14 | 15 | def sync_fn(): 16 | await = lambda: None 17 | async = lambda: None 18 | async() 19 | await() 20 | 21 | 22 | async def async_fn(): 23 | await async_fn() 24 | 25 | 26 | # output 27 | def async(): 28 | pass 29 | 30 | 31 | def await(): 32 | pass 33 | 34 | 35 | await = lambda: None 36 | async = lambda: None 37 | async() 38 | await() 39 | 40 | 41 | def sync_fn(): 42 | await = lambda: None 43 | async = lambda: None 44 | async() 45 | await() 46 | 47 | 48 | async def async_fn(): 49 | await async_fn() 50 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/blackd_diff.diff: -------------------------------------------------------------------------------- 1 | --- [Deterministic header] 2 | +++ [Deterministic header] 3 | @@ -1,6 +1,5 @@ 4 | -def abc (): 5 | - return ["hello", "world", 6 | - "!"] 7 | +def abc(): 8 | + return ["hello", "world", "!"] 9 | 10 | -print( "Incorrect formatting" 11 | -) 12 | + 13 | +print("Incorrect formatting") 14 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/blackd_diff.py: -------------------------------------------------------------------------------- 1 | def abc (): 2 | return ["hello", "world", 3 | "!"] 4 | 5 | print( "Incorrect formatting" 6 | ) 7 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/debug_visitor.py: -------------------------------------------------------------------------------- 1 | @dataclass 2 | class DebugVisitor(Visitor[T]): 3 | tree_depth: int = 0 4 | 5 | def visit_default(self, node: LN) -> Iterator[T]: 6 | indent = ' ' * (2 * self.tree_depth) 7 | if isinstance(node, Node): 8 | _type = type_repr(node.type) 9 | out(f'{indent}{_type}', fg='yellow') 10 | self.tree_depth += 1 11 | for child in node.children: 12 | yield from self.visit(child) 13 | 14 | self.tree_depth -= 1 15 | out(f'{indent}/{_type}', fg='yellow', bold=False) 16 | else: 17 | _type = token.tok_name.get(node.type, str(node.type)) 18 | out(f'{indent}{_type}', fg='blue', nl=False) 19 | if node.prefix: 20 | # We don't have to handle prefixes for `Node` objects since 21 | # that delegates to the first child anyway. 22 | out(f' {node.prefix!r}', fg='green', bold=False, nl=False) 23 | out(f' {node.value!r}', fg='blue', bold=False) 24 | 25 | @classmethod 26 | def show(cls, code: str) -> None: 27 | """Pretty-prints a given string of `code`. 28 | 29 | Convenience method for debugging. 30 | """ 31 | v: DebugVisitor[None] = DebugVisitor() 32 | list(v.visit(lib2to3_parse(code))) 33 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/force_py36.py: -------------------------------------------------------------------------------- 1 | # The input source must not contain any Py36-specific syntax (e.g. argument type 2 | # annotations, trailing comma after *rest) or this test becomes invalid. 3 | def long_function_name(argument_one, argument_two, argument_three, argument_four, argument_five, argument_six, *rest): pass 4 | # output 5 | # The input source must not contain any Py36-specific syntax (e.g. argument type 6 | # annotations, trailing comma after *rest) or this test becomes invalid. 7 | def long_function_name( 8 | argument_one, 9 | argument_two, 10 | argument_three, 11 | argument_four, 12 | argument_five, 13 | argument_six, 14 | *rest, 15 | ): 16 | pass 17 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/force_pyi.py: -------------------------------------------------------------------------------- 1 | # flags: --pyi 2 | from typing import Union 3 | 4 | @bird 5 | def zoo(): ... 6 | 7 | class A: ... 8 | @bar 9 | class B: 10 | def BMethod(self) -> None: ... 11 | @overload 12 | def BMethod(self, arg : List[str]) -> None: ... 13 | 14 | class C: ... 15 | @hmm 16 | class D: ... 17 | class E: ... 18 | 19 | @baz 20 | def foo() -> None: 21 | ... 22 | 23 | class F (A , C): ... 24 | def spam() -> None: ... 25 | 26 | @overload 27 | def spam(arg: str) -> str: ... 28 | 29 | var : int = 1 30 | 31 | def eggs() -> Union[str, int]: ... 32 | 33 | # output 34 | 35 | from typing import Union 36 | 37 | @bird 38 | def zoo(): ... 39 | 40 | class A: ... 41 | 42 | @bar 43 | class B: 44 | def BMethod(self) -> None: ... 45 | @overload 46 | def BMethod(self, arg: List[str]) -> None: ... 47 | 48 | class C: ... 49 | 50 | @hmm 51 | class D: ... 52 | 53 | class E: ... 54 | 55 | @baz 56 | def foo() -> None: ... 57 | 58 | class F(A, C): ... 59 | 60 | def spam() -> None: ... 61 | @overload 62 | def spam(arg: str) -> str: ... 63 | 64 | var: int = 1 65 | 66 | def eggs() -> Union[str, int]: ... 67 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/invalid_header.py: -------------------------------------------------------------------------------- 1 | This is not valid Python syntax 2 | y = "This is valid syntax" 3 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/missing_final_newline.diff: -------------------------------------------------------------------------------- 1 | --- [Deterministic header] 2 | +++ [Deterministic header] 3 | @@ -1,3 +1,3 @@ 4 | # A comment-only file, with no final EOL character 5 | # This triggers https://bugs.python.org/issue2142 6 | -# This is the line without the EOL character 7 | \ No newline at end of file 8 | +# This is the line without the EOL character 9 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/missing_final_newline.py: -------------------------------------------------------------------------------- 1 | # A comment-only file, with no final EOL character 2 | # This triggers https://bugs.python.org/issue2142 3 | # This is the line without the EOL character -------------------------------------------------------------------------------- /tests/data/miscellaneous/pattern_matching_invalid.py: -------------------------------------------------------------------------------- 1 | # First match, no errors 2 | match something: 3 | case bla(): 4 | pass 5 | 6 | # Problem on line 10 7 | match invalid_case: 8 | case valid_case: 9 | pass 10 | case a := b: 11 | pass 12 | case valid_case: 13 | pass 14 | 15 | # No problems either 16 | match something: 17 | case bla(): 18 | pass 19 | -------------------------------------------------------------------------------- /tests/data/miscellaneous/python2_detection.py: -------------------------------------------------------------------------------- 1 | # This uses a similar construction to the decorators.py test data file FYI. 2 | 3 | print "hello, world!" 4 | 5 | ### 6 | 7 | exec "print('hello, world!')" 8 | 9 | ### 10 | 11 | def set_position((x, y), value): 12 | pass 13 | 14 | ### 15 | 16 | try: 17 | pass 18 | except Exception, err: 19 | pass 20 | 21 | ### 22 | 23 | raise RuntimeError, "I feel like crashing today :p" 24 | 25 | ### 26 | 27 | `wow_these_really_did_exist` 28 | 29 | ### 30 | 31 | 10L 32 | 33 | ### 34 | 35 | 10l 36 | 37 | ### 38 | 39 | 0123 40 | 41 | # output 42 | 43 | print("hello python three!") 44 | 45 | ### 46 | 47 | exec("I'm not sure if you can use exec like this but that's not important here!") 48 | 49 | ### 50 | 51 | try: 52 | pass 53 | except make_exception(1, 2): 54 | pass 55 | 56 | ### 57 | 58 | try: 59 | pass 60 | except Exception as err: 61 | pass 62 | 63 | ### 64 | 65 | raise RuntimeError(make_msg(1, 2)) 66 | 67 | ### 68 | 69 | raise RuntimeError("boom!",) 70 | 71 | ### 72 | 73 | def set_position(x, y, value): 74 | pass 75 | 76 | ### 77 | 78 | 10 79 | 80 | ### 81 | 82 | 0 83 | 84 | ### 85 | 86 | 000 87 | 88 | ### 89 | 90 | 0o12 -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=41.0", "setuptools-scm", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/.gitignore: -------------------------------------------------------------------------------- 1 | a.py 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/a.py: -------------------------------------------------------------------------------- 1 | # should be excluded (root/.gitignore) 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/b.py: -------------------------------------------------------------------------------- 1 | # should be included 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/c.py: -------------------------------------------------------------------------------- 1 | # should be included 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/child/.gitignore: -------------------------------------------------------------------------------- 1 | b.py 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/child/a.py: -------------------------------------------------------------------------------- 1 | # should be excluded (root/.gitignore) 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/child/b.py: -------------------------------------------------------------------------------- 1 | # should be excluded (child/.gitignore) 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/root/child/c.py: -------------------------------------------------------------------------------- 1 | # should be included 2 | -------------------------------------------------------------------------------- /tests/data/nested_gitignore_tests/x.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psf/black/e7bf7b4619928da69d486f36fcb456fb201ff53e/tests/data/nested_gitignore_tests/x.py -------------------------------------------------------------------------------- /tests/data/project_metadata/both_pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "test" 3 | version = "1.0.0" 4 | requires-python = ">=3.7,<3.11" 5 | 6 | [tool.black] 7 | line-length = 79 8 | target-version = ["py310"] 9 | -------------------------------------------------------------------------------- /tests/data/project_metadata/neither_pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "test" 3 | version = "1.0.0" 4 | 5 | [tool.black] 6 | line-length = 79 7 | -------------------------------------------------------------------------------- /tests/data/project_metadata/only_black_pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "test" 3 | version = "1.0.0" 4 | 5 | [tool.black] 6 | line-length = 79 7 | target-version = ["py310"] 8 | -------------------------------------------------------------------------------- /tests/data/project_metadata/only_metadata_pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "test" 3 | version = "1.0.0" 4 | requires-python = ">=3.7,<3.11" 5 | 6 | [tool.black] 7 | line-length = 79 8 | -------------------------------------------------------------------------------- /tests/empty.toml: -------------------------------------------------------------------------------- 1 | # Empty configuration file; used in tests to avoid interference from Black's own config. 2 | -------------------------------------------------------------------------------- /tests/test.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | verbose = 1 3 | --check = "no" 4 | diff = "y" 5 | color = true 6 | line-length = 79 7 | target-version = ["py36", "py37", "py38"] 8 | exclude='\.pyi?$' 9 | include='\.py?$' 10 | python-cell-magics = ["custom1", "custom2"] 11 | 12 | [v1.0.0-syntax] 13 | # This shouldn't break Black. 14 | contributors = [ 15 | "Foo Bar ", 16 | { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } 17 | ] 18 | -------------------------------------------------------------------------------- /tests/test_no_ipynb.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | import pytest 4 | from click.testing import CliRunner 5 | 6 | from black import jupyter_dependencies_are_installed, main 7 | from tests.util import get_case_path 8 | 9 | pytestmark = pytest.mark.no_jupyter 10 | 11 | runner = CliRunner() 12 | 13 | 14 | def test_ipynb_diff_with_no_change_single() -> None: 15 | jupyter_dependencies_are_installed.cache_clear() 16 | path = get_case_path("jupyter", "notebook_trailing_newline.ipynb") 17 | result = runner.invoke(main, [str(path)]) 18 | expected_output = ( 19 | "Skipping .ipynb files as Jupyter dependencies are not installed.\n" 20 | 'You can fix this by running ``pip install "black[jupyter]"``\n' 21 | ) 22 | assert expected_output in result.output 23 | 24 | 25 | def test_ipynb_diff_with_no_change_dir(tmp_path: pathlib.Path) -> None: 26 | jupyter_dependencies_are_installed.cache_clear() 27 | runner = CliRunner() 28 | nb = get_case_path("jupyter", "notebook_trailing_newline.ipynb") 29 | tmp_nb = tmp_path / "notebook.ipynb" 30 | tmp_nb.write_bytes(nb.read_bytes()) 31 | result = runner.invoke(main, [str(tmp_path)]) 32 | expected_output = ( 33 | "Skipping .ipynb files as Jupyter dependencies are not installed.\n" 34 | 'You can fix this by running ``pip install "black[jupyter]"``\n' 35 | ) 36 | assert expected_output in result.output 37 | -------------------------------------------------------------------------------- /tests/test_schema.py: -------------------------------------------------------------------------------- 1 | import importlib.metadata 2 | import sys 3 | 4 | 5 | def test_schema_entrypoint() -> None: 6 | if sys.version_info < (3, 10): 7 | eps = importlib.metadata.entry_points()["validate_pyproject.tool_schema"] 8 | (black_ep,) = [ep for ep in eps if ep.name == "black"] 9 | else: 10 | (black_ep,) = importlib.metadata.entry_points( 11 | group="validate_pyproject.tool_schema", name="black" 12 | ) 13 | 14 | black_fn = black_ep.load() 15 | schema = black_fn() 16 | assert schema == black_fn("black") 17 | assert schema["properties"]["line-length"]["type"] == "integer" 18 | -------------------------------------------------------------------------------- /tests/test_trans.py: -------------------------------------------------------------------------------- 1 | from black.trans import iter_fexpr_spans 2 | 3 | 4 | def test_fexpr_spans() -> None: 5 | def check( 6 | string: str, expected_spans: list[tuple[int, int]], expected_slices: list[str] 7 | ) -> None: 8 | spans = list(iter_fexpr_spans(string)) 9 | 10 | # Checking slices isn't strictly necessary, but it's easier to verify at 11 | # a glance than only spans 12 | assert len(spans) == len(expected_slices) 13 | for (i, j), slice in zip(spans, expected_slices): 14 | assert 0 <= i <= j <= len(string) 15 | assert string[i:j] == slice 16 | 17 | assert spans == expected_spans 18 | 19 | # Most of these test cases omit the leading 'f' and leading / closing quotes 20 | # for convenience 21 | # Some additional property-based tests can be found in 22 | # https://github.com/psf/black/pull/2654#issuecomment-981411748 23 | check("""{var}""", [(0, 5)], ["{var}"]) 24 | check("""f'{var}'""", [(2, 7)], ["{var}"]) 25 | check("""f'{1 + f() + 2 + "asdf"}'""", [(2, 24)], ["""{1 + f() + 2 + "asdf"}"""]) 26 | check("""text {var} text""", [(5, 10)], ["{var}"]) 27 | check("""text {{ {var} }} text""", [(8, 13)], ["{var}"]) 28 | check("""{a} {b} {c}""", [(0, 3), (4, 7), (8, 11)], ["{a}", "{b}", "{c}"]) 29 | check("""f'{a} {b} {c}'""", [(2, 5), (6, 9), (10, 13)], ["{a}", "{b}", "{c}"]) 30 | check("""{ {} }""", [(0, 6)], ["{ {} }"]) 31 | check("""{ {{}} }""", [(0, 8)], ["{ {{}} }"]) 32 | check("""{ {{{}}} }""", [(0, 10)], ["{ {{{}}} }"]) 33 | check("""{{ {{{}}} }}""", [(5, 7)], ["{}"]) 34 | check("""{{ {{{var}}} }}""", [(5, 10)], ["{var}"]) 35 | check("""{f"{0}"}""", [(0, 8)], ["""{f"{0}"}"""]) 36 | check("""{"'"}""", [(0, 5)], ["""{"'"}"""]) 37 | check("""{"{"}""", [(0, 5)], ["""{"{"}"""]) 38 | check("""{"}"}""", [(0, 5)], ["""{"}"}"""]) 39 | check("""{"{{"}""", [(0, 6)], ["""{"{{"}"""]) 40 | check("""{''' '''}""", [(0, 9)], ["""{''' '''}"""]) 41 | check("""{'''{'''}""", [(0, 9)], ["""{'''{'''}"""]) 42 | check("""{''' {'{ '''}""", [(0, 13)], ["""{''' {'{ '''}"""]) 43 | check( 44 | '''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-'y\\'\'\'\'''', 45 | [(5, 33)], 46 | ['''{f"""*{f"+{f'.{x}.'}+"}*"""}'''], 47 | ) 48 | check(r"""{}{""", [(0, 2)], ["{}"]) 49 | check("""f"{'{'''''''''}\"""", [(2, 15)], ["{'{'''''''''}"]) 50 | --------------------------------------------------------------------------------