A transformer which normalizes spacing around power operators.
(
line: Line, features: Collection[Feature], mode: Mode
)
| 68 | |
| 69 | # Remove when `simplify_power_operator_hugging` becomes stable. |
| 70 | def hug_power_op( |
| 71 | line: Line, features: Collection[Feature], mode: Mode |
| 72 | ) -> Iterator[Line]: |
| 73 | """A transformer which normalizes spacing around power operators.""" |
| 74 | |
| 75 | # Performance optimization to avoid unnecessary Leaf clones and other ops. |
| 76 | for leaf in line.leaves: |
| 77 | if leaf.type == token.DOUBLESTAR: |
| 78 | break |
| 79 | else: |
| 80 | raise CannotTransform("No doublestar token was found in the line.") |
| 81 | |
| 82 | def is_simple_lookup(index: int, kind: Literal[1, -1]) -> bool: |
| 83 | # Brackets and parentheses indicate calls, subscripts, etc. ... |
| 84 | # basically stuff that doesn't count as "simple". Only a NAME lookup |
| 85 | # or dotted lookup (eg. NAME.NAME) is OK. |
| 86 | if kind == -1: |
| 87 | return handle_is_simple_look_up_prev(line, index, {token.RPAR, token.RSQB}) |
| 88 | else: |
| 89 | return handle_is_simple_lookup_forward( |
| 90 | line, index, {token.LPAR, token.LSQB} |
| 91 | ) |
| 92 | |
| 93 | def is_simple_operand(index: int, kind: Literal[1, -1]) -> bool: |
| 94 | # An operand is considered "simple" if's a NAME, a numeric CONSTANT, a simple |
| 95 | # lookup (see above), with or without a preceding unary operator. |
| 96 | start = line.leaves[index] |
| 97 | if start.type in {token.NAME, token.NUMBER}: |
| 98 | return is_simple_lookup(index, kind) |
| 99 | |
| 100 | if start.type in {token.PLUS, token.MINUS, token.TILDE}: |
| 101 | if line.leaves[index + 1].type in {token.NAME, token.NUMBER}: |
| 102 | # kind is always one as bases with a preceding unary op will be checked |
| 103 | # for simplicity starting from the next token (so it'll hit the check |
| 104 | # above). |
| 105 | return is_simple_lookup(index + 1, kind=1) |
| 106 | |
| 107 | return False |
| 108 | |
| 109 | new_line = line.clone() |
| 110 | should_hug = False |
| 111 | for idx, leaf in enumerate(line.leaves): |
| 112 | new_leaf = leaf.clone() |
| 113 | if should_hug: |
| 114 | new_leaf.prefix = "" |
| 115 | should_hug = False |
| 116 | |
| 117 | should_hug = ( |
| 118 | (0 < idx < len(line.leaves) - 1) |
| 119 | and leaf.type == token.DOUBLESTAR |
| 120 | and is_simple_operand(idx - 1, kind=-1) |
| 121 | and line.leaves[idx - 1].value != "lambda" |
| 122 | and is_simple_operand(idx + 1, kind=1) |
| 123 | ) |
| 124 | if should_hug: |
| 125 | new_leaf.prefix = "" |
| 126 | |
| 127 | # We have to be careful to make a new line properly: |
no test coverage detected