From 6417c99bfdbdc057e4a10aeff9967a751f4f85e9 Mon Sep 17 00:00:00 2001 From: Richard Si <63936253+ichard26@users.noreply.github.com> Date: Mon, 24 Jan 2022 22:13:34 -0500 Subject: [PATCH] Hug power operators if its operands are "simple" (#2726) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since power operators almost always have the highest binding power in expressions, it's often more readable to hug it with its operands. The main exception to this is when its operands are non-trivial in which case the power operator will not hug, the rule for this is the following: > For power ops, an operand is considered "simple" if it's only a NAME, numeric CONSTANT, or attribute access (chained attribute access is allowed), with or without a preceding unary operator. Fixes GH-538. Closes GH-2095. diff-shades results: https://gist.github.com/ichard26/ca6c6ad4bd1de5152d95418c8645354b Co-authored-by: Diego Co-authored-by: Felix Hildén Co-authored-by: Jelle Zijlstra --- CHANGES.md | 1 + docs/the_black_code_style/current_style.md | 20 ++++ src/black/linegen.py | 7 +- src/black/trans.py | 86 ++++++++++++++- src/black_primer/primer.json | 2 +- tests/data/expression.diff | 44 +++++--- tests/data/expression.py | 30 ++--- .../expression_skip_magic_trailing_comma.diff | 44 +++++--- tests/data/pep_572.py | 2 +- tests/data/pep_572_py39.py | 2 +- tests/data/power_op_spacing.py | 103 ++++++++++++++++++ tests/data/slices.py | 2 +- tests/test_format.py | 1 + 13 files changed, 293 insertions(+), 51 deletions(-) create mode 100644 tests/data/power_op_spacing.py diff --git a/CHANGES.md b/CHANGES.md index 458d48cd2c1..d203896a801 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ - Tuple unpacking on `return` and `yield` constructs now implies 3.8+ (#2700) - Unparenthesized tuples on annotated assignments (e.g `values: Tuple[int, ...] = 1, 2, 3`) now implies 3.8+ (#2708) +- Remove spaces around power operators if both operands are simple (#2726) - Allow setting custom cache directory on all platforms with environment variable `BLACK_CACHE_DIR` (#2739). - Text coloring added in the final statistics (#2712) diff --git a/docs/the_black_code_style/current_style.md b/docs/the_black_code_style/current_style.md index 1d1e42e75c8..5be7ba6dbdb 100644 --- a/docs/the_black_code_style/current_style.md +++ b/docs/the_black_code_style/current_style.md @@ -284,6 +284,26 @@ multiple lines. This is so that _Black_ is compliant with the recent changes in [PEP 8](https://www.python.org/dev/peps/pep-0008/#should-a-line-break-before-or-after-a-binary-operator) style guide, which emphasizes that this approach improves readability. +Almost all operators will be surrounded by single spaces, the only exceptions are unary +operators (`+`, `-`, and `~`), and power operators when both operands are simple. For +powers, an operand is considered simple if it's only a NAME, numeric CONSTANT, or +attribute access (chained attribute access is allowed), with or without a preceding +unary operator. + +```python +# For example, these won't be surrounded by whitespace +a = x**y +b = config.base**5.2 +c = config.base**runtime.config.exponent +d = 2**5 +e = 2**~5 + +# ... but these will be surrounded by whitespace +f = 2 ** get_exponent() +g = get_x() ** get_y() +h = config['base'] ** 2 +``` + ### Slices PEP 8 diff --git a/src/black/linegen.py b/src/black/linegen.py index 9ee42aaaf72..9fbdfadba6a 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -21,8 +21,8 @@ from black.numerics import normalize_numeric_literal from black.strings import get_string_prefix, fix_docstring from black.strings import normalize_string_prefix, normalize_string_quotes -from black.trans import Transformer, CannotTransform, StringMerger -from black.trans import StringSplitter, StringParenWrapper, StringParenStripper +from black.trans import Transformer, CannotTransform, StringMerger, StringSplitter +from black.trans import StringParenWrapper, StringParenStripper, hug_power_op from black.mode import Mode, Feature, Preview from blib2to3.pytree import Node, Leaf @@ -404,6 +404,9 @@ def _rhs( transformers = [delimiter_split, standalone_comment_split, rhs] else: transformers = [rhs] + # It's always safe to attempt hugging of power operations and pretty much every line + # could match. + transformers.append(hug_power_op) for transform in transformers: # We are accumulating lines in `result` because we might want to abort diff --git a/src/black/trans.py b/src/black/trans.py index cb41c1be487..74d052fe2dc 100644 --- a/src/black/trans.py +++ b/src/black/trans.py @@ -24,9 +24,9 @@ import sys if sys.version_info < (3, 8): - from typing_extensions import Final + from typing_extensions import Literal, Final else: - from typing import Final + from typing import Literal, Final from mypy_extensions import trait @@ -71,6 +71,88 @@ def TErr(err_msg: str) -> Err[CannotTransform]: return Err(cant_transform) +def hug_power_op(line: Line, features: Collection[Feature]) -> Iterator[Line]: + """A transformer which normalizes spacing around power operators.""" + + # Performance optimization to avoid unnecessary Leaf clones and other ops. + for leaf in line.leaves: + if leaf.type == token.DOUBLESTAR: + break + else: + raise CannotTransform("No doublestar token was found in the line.") + + def is_simple_lookup(index: int, step: Literal[1, -1]) -> bool: + # Brackets and parentheses indicate calls, subscripts, etc. ... + # basically stuff that doesn't count as "simple". Only a NAME lookup + # or dotted lookup (eg. NAME.NAME) is OK. + if step == -1: + disallowed = {token.RPAR, token.RSQB} + else: + disallowed = {token.LPAR, token.LSQB} + + while 0 <= index < len(line.leaves): + current = line.leaves[index] + if current.type in disallowed: + return False + if current.type not in {token.NAME, token.DOT} or current.value == "for": + # If the current token isn't disallowed, we'll assume this is simple as + # only the disallowed tokens are semantically attached to this lookup + # expression we're checking. Also, stop early if we hit the 'for' bit + # of a comprehension. + return True + + index += step + + return True + + def is_simple_operand(index: int, kind: Literal["base", "exponent"]) -> bool: + # An operand is considered "simple" if's a NAME, a numeric CONSTANT, a simple + # lookup (see above), with or without a preceding unary operator. + start = line.leaves[index] + if start.type in {token.NAME, token.NUMBER}: + return is_simple_lookup(index, step=(1 if kind == "exponent" else -1)) + + if start.type in {token.PLUS, token.MINUS, token.TILDE}: + if line.leaves[index + 1].type in {token.NAME, token.NUMBER}: + # step is always one as bases with a preceding unary op will be checked + # for simplicity starting from the next token (so it'll hit the check + # above). + return is_simple_lookup(index + 1, step=1) + + return False + + leaves: List[Leaf] = [] + should_hug = False + for idx, leaf in enumerate(line.leaves): + new_leaf = leaf.clone() + if should_hug: + new_leaf.prefix = "" + should_hug = False + + should_hug = ( + (0 < idx < len(line.leaves) - 1) + and leaf.type == token.DOUBLESTAR + and is_simple_operand(idx - 1, kind="base") + and line.leaves[idx - 1].value != "lambda" + and is_simple_operand(idx + 1, kind="exponent") + ) + if should_hug: + new_leaf.prefix = "" + + leaves.append(new_leaf) + + yield Line( + mode=line.mode, + depth=line.depth, + leaves=leaves, + comments=line.comments, + bracket_tracker=line.bracket_tracker, + inside_brackets=line.inside_brackets, + should_split_rhs=line.should_split_rhs, + magic_trailing_comma=line.magic_trailing_comma, + ) + + class StringTransformer(ABC): """ An implementation of the Transformer protocol that relies on its diff --git a/src/black_primer/primer.json b/src/black_primer/primer.json index a8d8fc9e21f..a6bfd4a2fec 100644 --- a/src/black_primer/primer.json +++ b/src/black_primer/primer.json @@ -81,7 +81,7 @@ }, "flake8-bugbear": { "cli_arguments": ["--experimental-string-processing"], - "expect_formatting_changes": false, + "expect_formatting_changes": true, "git_clone_url": "https://github.com/PyCQA/flake8-bugbear.git", "long_checkout": false, "py_versions": ["all"] diff --git a/tests/data/expression.diff b/tests/data/expression.diff index 721a07d2141..5f29a18dc7f 100644 --- a/tests/data/expression.diff +++ b/tests/data/expression.diff @@ -11,7 +11,17 @@ True False 1 -@@ -29,63 +29,96 @@ +@@ -21,71 +21,104 @@ + Name1 or (Name2 and Name3) or Name4 + Name1 or Name2 and Name3 or Name4 + v1 << 2 + 1 >> v2 + 1 % finished +-1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8 +-((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8) ++1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8 ++((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8) + not great ~great +value -1 @@ -19,7 +29,7 @@ (~int) and (not ((v1 ^ (123 + v2)) | True)) -+really ** -confusing ** ~operator ** -precedence -flags & ~ select.EPOLLIN and waiters.write_task is not None -++(really ** -(confusing ** ~(operator ** -precedence))) +++(really ** -(confusing ** ~(operator**-precedence))) +flags & ~select.EPOLLIN and waiters.write_task is not None lambda arg: None lambda a=True: a @@ -88,15 +98,19 @@ + *more, +] {i for i in (1, 2, 3)} - {(i ** 2) for i in (1, 2, 3)} +-{(i ** 2) for i in (1, 2, 3)} -{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))} -+{(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} - {((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} +-{((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} ++{(i**2) for i in (1, 2, 3)} ++{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} ++{((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} [i for i in (1, 2, 3)] - [(i ** 2) for i in (1, 2, 3)] +-[(i ** 2) for i in (1, 2, 3)] -[(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))] -+[(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] - [((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] +-[((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] ++[(i**2) for i in (1, 2, 3)] ++[(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] ++[((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] {i: 0 for i in (1, 2, 3)} -{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))} +{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} @@ -181,10 +195,12 @@ SomeName (Good, Bad, Ugly) (i for i in (1, 2, 3)) - ((i ** 2) for i in (1, 2, 3)) +-((i ** 2) for i in (1, 2, 3)) -((i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))) -+((i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) - (((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) +-(((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) ++((i**2) for i in (1, 2, 3)) ++((i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) ++(((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) (*starred,) -{"id": "1","type": "type","started_at": now(),"ended_at": now() + timedelta(days=10),"priority": 1,"import_session_id": 1,**kwargs} +{ @@ -403,13 +419,13 @@ + return True +if ( + ~aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e -+ | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l ** aaaa.m // aaaa.n ++ | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n +): + return True +if ( + ~aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e + | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h -+ ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l ** aaaaaaaa.m // aaaaaaaa.n ++ ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n +): + return True +if ( @@ -419,7 +435,7 @@ + | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h + ^ aaaaaaaaaaaaaaaa.i + << aaaaaaaaaaaaaaaa.k -+ >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n ++ >> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n +): + return True +( diff --git a/tests/data/expression.py b/tests/data/expression.py index d13450cda68..b056841027d 100644 --- a/tests/data/expression.py +++ b/tests/data/expression.py @@ -282,15 +282,15 @@ async def f(): v1 << 2 1 >> v2 1 % finished -1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8 -((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8) +1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8 +((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8) not great ~great +value -1 ~int and not v1 ^ 123 + v2 | True (~int) and (not ((v1 ^ (123 + v2)) | True)) -+(really ** -(confusing ** ~(operator ** -precedence))) ++(really ** -(confusing ** ~(operator**-precedence))) flags & ~select.EPOLLIN and waiters.write_task is not None lambda arg: None lambda a=True: a @@ -347,13 +347,13 @@ async def f(): *more, ] {i for i in (1, 2, 3)} -{(i ** 2) for i in (1, 2, 3)} -{(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} -{((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} +{(i**2) for i in (1, 2, 3)} +{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} +{((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} [i for i in (1, 2, 3)] -[(i ** 2) for i in (1, 2, 3)] -[(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] -[((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] +[(i**2) for i in (1, 2, 3)] +[(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] +[((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] {i: 0 for i in (1, 2, 3)} {i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} {a: b * 2 for a, b in dictionary.items()} @@ -441,9 +441,9 @@ async def f(): SomeName (Good, Bad, Ugly) (i for i in (1, 2, 3)) -((i ** 2) for i in (1, 2, 3)) -((i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) -(((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) +((i**2) for i in (1, 2, 3)) +((i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) +(((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) (*starred,) { "id": "1", @@ -588,13 +588,13 @@ async def f(): return True if ( ~aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e - | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l ** aaaa.m // aaaa.n + | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n ): return True if ( ~aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h - ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l ** aaaaaaaa.m // aaaaaaaa.n + ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n ): return True if ( @@ -604,7 +604,7 @@ async def f(): | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k - >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n + >> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n ): return True ( diff --git a/tests/data/expression_skip_magic_trailing_comma.diff b/tests/data/expression_skip_magic_trailing_comma.diff index 4a8a95c7237..5b722c91352 100644 --- a/tests/data/expression_skip_magic_trailing_comma.diff +++ b/tests/data/expression_skip_magic_trailing_comma.diff @@ -11,7 +11,17 @@ True False 1 -@@ -29,63 +29,84 @@ +@@ -21,71 +21,92 @@ + Name1 or (Name2 and Name3) or Name4 + Name1 or Name2 and Name3 or Name4 + v1 << 2 + 1 >> v2 + 1 % finished +-1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8 +-((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8) ++1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8 ++((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8) + not great ~great +value -1 @@ -19,7 +29,7 @@ (~int) and (not ((v1 ^ (123 + v2)) | True)) -+really ** -confusing ** ~operator ** -precedence -flags & ~ select.EPOLLIN and waiters.write_task is not None -++(really ** -(confusing ** ~(operator ** -precedence))) +++(really ** -(confusing ** ~(operator**-precedence))) +flags & ~select.EPOLLIN and waiters.write_task is not None lambda arg: None lambda a=True: a @@ -76,15 +86,19 @@ + *more, +] {i for i in (1, 2, 3)} - {(i ** 2) for i in (1, 2, 3)} +-{(i ** 2) for i in (1, 2, 3)} -{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))} -+{(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} - {((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} +-{((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} ++{(i**2) for i in (1, 2, 3)} ++{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))} ++{((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)} [i for i in (1, 2, 3)] - [(i ** 2) for i in (1, 2, 3)] +-[(i ** 2) for i in (1, 2, 3)] -[(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))] -+[(i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] - [((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] +-[((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] ++[(i**2) for i in (1, 2, 3)] ++[(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))] ++[((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)] {i: 0 for i in (1, 2, 3)} -{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))} +{i: j for i, j in ((1, "a"), (2, "b"), (3, "c"))} @@ -164,10 +178,12 @@ SomeName (Good, Bad, Ugly) (i for i in (1, 2, 3)) - ((i ** 2) for i in (1, 2, 3)) +-((i ** 2) for i in (1, 2, 3)) -((i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))) -+((i ** 2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) - (((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) +-(((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) ++((i**2) for i in (1, 2, 3)) ++((i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))) ++(((i**2) + j) for i in (1, 2, 3) for j in (1, 2, 3)) (*starred,) -{"id": "1","type": "type","started_at": now(),"ended_at": now() + timedelta(days=10),"priority": 1,"import_session_id": 1,**kwargs} +{ @@ -384,13 +400,13 @@ + return True +if ( + ~aaaa.a + aaaa.b - aaaa.c * aaaa.d / aaaa.e -+ | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l ** aaaa.m // aaaa.n ++ | aaaa.f & aaaa.g % aaaa.h ^ aaaa.i << aaaa.k >> aaaa.l**aaaa.m // aaaa.n +): + return True +if ( + ~aaaaaaaa.a + aaaaaaaa.b - aaaaaaaa.c @ aaaaaaaa.d / aaaaaaaa.e + | aaaaaaaa.f & aaaaaaaa.g % aaaaaaaa.h -+ ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l ** aaaaaaaa.m // aaaaaaaa.n ++ ^ aaaaaaaa.i << aaaaaaaa.k >> aaaaaaaa.l**aaaaaaaa.m // aaaaaaaa.n +): + return True +if ( @@ -400,7 +416,7 @@ + | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h + ^ aaaaaaaaaaaaaaaa.i + << aaaaaaaaaaaaaaaa.k -+ >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n ++ >> aaaaaaaaaaaaaaaa.l**aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n +): + return True +( diff --git a/tests/data/pep_572.py b/tests/data/pep_572.py index c6867f26258..d41805f1cb1 100644 --- a/tests/data/pep_572.py +++ b/tests/data/pep_572.py @@ -4,7 +4,7 @@ pass if match := pattern.search(data): pass -[y := f(x), y ** 2, y ** 3] +[y := f(x), y**2, y**3] filtered_data = [y for x in data if (y := f(x)) is None] (y := f(x)) y0 = (y1 := f(x)) diff --git a/tests/data/pep_572_py39.py b/tests/data/pep_572_py39.py index 7bbd5091197..b8b081b8c45 100644 --- a/tests/data/pep_572_py39.py +++ b/tests/data/pep_572_py39.py @@ -1,7 +1,7 @@ # Unparenthesized walruses are now allowed in set literals & set comprehensions # since Python 3.9 {x := 1, 2, 3} -{x4 := x ** 5 for x in range(7)} +{x4 := x**5 for x in range(7)} # We better not remove the parentheses here (since it's a 3.10 feature) x[(a := 1)] x[(a := 1), (b := 3)] diff --git a/tests/data/power_op_spacing.py b/tests/data/power_op_spacing.py new file mode 100644 index 00000000000..87dde7f39dc --- /dev/null +++ b/tests/data/power_op_spacing.py @@ -0,0 +1,103 @@ +def function(**kwargs): + t = a**2 + b**3 + return t ** 2 + + +def function_replace_spaces(**kwargs): + t = a **2 + b** 3 + c ** 4 + + +def function_dont_replace_spaces(): + {**a, **b, **c} + + +a = 5**~4 +b = 5 ** f() +c = -(5**2) +d = 5 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5 +g = a.b**c.d +h = 5 ** funcs.f() +i = funcs.f() ** 5 +j = super().name ** 5 +k = [(2**idx, value) for idx, value in pairs] +l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) +m = [([2**63], [1, 2**63])] +n = count <= 10**5 +o = settings(max_examples=10**6) +p = {(k, k**2): v**2 for k, v in pairs} +q = [10**i for i in range(6)] +r = x**y + +a = 5.0**~4.0 +b = 5.0 ** f() +c = -(5.0**2.0) +d = 5.0 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5.0 +g = a.b**c.d +h = 5.0 ** funcs.f() +i = funcs.f() ** 5.0 +j = super().name ** 5.0 +k = [(2.0**idx, value) for idx, value in pairs] +l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) +m = [([2.0**63.0], [1.0, 2**63.0])] +n = count <= 10**5.0 +o = settings(max_examples=10**6.0) +p = {(k, k**2): v**2.0 for k, v in pairs} +q = [10.5**i for i in range(6)] + + +# output + + +def function(**kwargs): + t = a**2 + b**3 + return t**2 + + +def function_replace_spaces(**kwargs): + t = a**2 + b**3 + c**4 + + +def function_dont_replace_spaces(): + {**a, **b, **c} + + +a = 5**~4 +b = 5 ** f() +c = -(5**2) +d = 5 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5 +g = a.b**c.d +h = 5 ** funcs.f() +i = funcs.f() ** 5 +j = super().name ** 5 +k = [(2**idx, value) for idx, value in pairs] +l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) +m = [([2**63], [1, 2**63])] +n = count <= 10**5 +o = settings(max_examples=10**6) +p = {(k, k**2): v**2 for k, v in pairs} +q = [10**i for i in range(6)] +r = x**y + +a = 5.0**~4.0 +b = 5.0 ** f() +c = -(5.0**2.0) +d = 5.0 ** f["hi"] +e = lazy(lambda **kwargs: 5) +f = f() ** 5.0 +g = a.b**c.d +h = 5.0 ** funcs.f() +i = funcs.f() ** 5.0 +j = super().name ** 5.0 +k = [(2.0**idx, value) for idx, value in pairs] +l = mod.weights_[0] == pytest.approx(0.95**100, abs=0.001) +m = [([2.0**63.0], [1.0, 2**63.0])] +n = count <= 10**5.0 +o = settings(max_examples=10**6.0) +p = {(k, k**2): v**2.0 for k, v in pairs} +q = [10.5**i for i in range(6)] diff --git a/tests/data/slices.py b/tests/data/slices.py index 7a42678f646..165117cdcb4 100644 --- a/tests/data/slices.py +++ b/tests/data/slices.py @@ -9,7 +9,7 @@ slice[:c, c - 1] slice[c, c + 1, d::] slice[ham[c::d] :: 1] -slice[ham[cheese ** 2 : -1] : 1 : 1, ham[1:2]] +slice[ham[cheese**2 : -1] : 1 : 1, ham[1:2]] slice[:-1:] slice[lambda: None : lambda: None] slice[lambda x, y, *args, really=2, **kwargs: None :, None::] diff --git a/tests/test_format.py b/tests/test_format.py index 3895a095e86..c6c811040dc 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -48,6 +48,7 @@ "function2", "function_trailing_comma", "import_spacing", + "power_op_spacing", "remove_parens", "slices", "string_prefixes",