├── .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 |
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 |
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 |
--------------------------------------------------------------------------------