Skip to content

Commit

Permalink
Merge branch 'main' into arithmetic-stability
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra committed Jan 28, 2022
2 parents ae71c47 + 5f01b87 commit 69e10a8
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 44 deletions.
78 changes: 49 additions & 29 deletions CHANGES.md
Expand Up @@ -2,60 +2,79 @@

## Unreleased

### _Black_
At long last, _Black_ is no longer a beta product! This is the first non-beta release
and the first release covered by our new stability policy.

### Highlights

- **Remove Python 2 support** (#2740)
- Do not accept bare carriage return line endings in pyproject.toml (#2408)
- Improve error message for invalid regular expression (#2678)
- Improve error message when parsing fails during AST safety check by embedding the
underlying SyntaxError (#2693)
- Introduce the `--preview` flag (#2752)

### Style

- Deprecate `--experimental-string-processing` and move the functionality under
`--preview` (#2789)
- For stubs, one blank line between class attributes and methods is now kept if there's
at least one pre-existing blank line (#2736)
- Black now normalizes string prefix order (#2297)
- Remove spaces around power operators if both operands are simple (#2726)
- Work around bug that causes unstable formatting in some cases in the presence of the
magic trailing comma (#2807)
- Use parentheses for attribute access on decimal float and int literals (#2799)
- Don't add whitespace for attribute access on hexadecimal, binary, octal, and complex
literals (#2799)
- Fix unstable formatting with semicolons and arithmetic expressions (#2817)

### Parser

- Fix mapping cases that contain as-expressions, like `case {"key": 1 | 2 as password}`
(#2686)
- Fix cases that contain multiple top-level as-expressions, like `case 1 as a, 2 as b`
(#2716)
- Fix call patterns that contain as-expressions with keyword arguments, like
`case Foo(bar=baz as quux)` (#2749)
- No longer color diff headers white as it's unreadable in light themed terminals
(#2691)
- 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)
- For stubs, one blank line between class attributes and methods is now kept if there's
at least one pre-existing blank line (#2736)
- Verbose mode also now describes how a project root was discovered and which paths will
be formatted. (#2526)
- Speed-up the new backtracking parser about 4X in general (enabled when
`--target-version` is set to 3.10 and higher). (#2728)
- Fix handling of standalone `match()` or `case()` when there is a trailing newline or a
comment inside of the parentheses. (#2760)
- Black now normalizes string prefix order (#2297)

### Performance

- Speed-up the new backtracking parser about 4X in general (enabled when
`--target-version` is set to 3.10 and higher). (#2728)
- _Black_ is now compiled with [mypyc](https://github.com/mypyc/mypyc) for an overall 2x
speed-up. 64-bit Windows, MacOS, and Linux (not including musl) are supported. (#1009,
#2431)

### Configuration

- Do not accept bare carriage return line endings in pyproject.toml (#2408)
- Add configuration option (`python-cell-magics`) to format cells with custom magics in
Jupyter Notebooks (#2744)
- Deprecate `--experimental-string-processing` and move the functionality under
`--preview` (#2789)
- Allow setting custom cache directory on all platforms with environment variable
`BLACK_CACHE_DIR` (#2739).
- Enable Python 3.10+ by default, without any extra need to specify
`--target-version=py310`. (#2758)
- Make passing `SRC` or `--code` mandatory and mutually exclusive (#2804)
- Work around bug that causes unstable formatting in some cases in the presence of the
magic trailing comma (#2807)
- Deprecate the `black-primer` tool (#2809)
- Fix unstable formatting with semicolons and arithmetic expressions (#2817)

### Output

- Improve error message for invalid regular expression (#2678)
- Improve error message when parsing fails during AST safety check by embedding the
underlying SyntaxError (#2693)
- No longer color diff headers white as it's unreadable in light themed terminals
(#2691)
- Text coloring added in the final statistics (#2712)
- Verbose mode also now describes how a project root was discovered and which paths will
be formatted. (#2526)

### Packaging

- All upper version bounds on dependencies have been removed (#2718)
- `typing-extensions` is no longer a required dependency in Python 3.10+ (#2772)
- Set `click` lower bound to `8.0.0` (#2791)

### Preview style

- Introduce the `--preview` flag (#2752)
- Add `--experimental-string-processing` to the preview style (#2789)

### Integrations

- Update GitHub action to support containerized runs (#2748)
Expand All @@ -65,6 +84,7 @@
- Change protocol in pip installation instructions to `https://` (#2761)
- Change HTML theme to Furo primarily for its responsive design and mobile support
(#2793)
- Deprecate the `black-primer` tool (#2809)

## 21.12b0

Expand Down
2 changes: 1 addition & 1 deletion fuzz.py
Expand Up @@ -48,7 +48,7 @@ def test_idempotent_any_syntatically_valid_python(
dst_contents = black.format_str(src_contents, mode=mode)
except black.InvalidInput:
# This is a bug - if it's valid Python code, as above, Black should be
# able to cope with it. See issues #970, #1012, #1358, and #1557.
# able to cope with it. See issues #970, #1012
# TODO: remove this try-except block when issues are resolved.
return
except TokenError as e:
Expand Down
22 changes: 22 additions & 0 deletions src/black/linegen.py
Expand Up @@ -201,6 +201,28 @@ def visit_decorators(self, node: Node) -> Iterator[Line]:
yield from self.line()
yield from self.visit(child)

def visit_power(self, node: Node) -> Iterator[Line]:
for idx, leaf in enumerate(node.children[:-1]):
next_leaf = node.children[idx + 1]

if not isinstance(leaf, Leaf):
continue

value = leaf.value.lower()
if (
leaf.type == token.NUMBER
and next_leaf.type == syms.trailer
# Ensure that we are in an attribute trailer
and next_leaf.children[0].type == token.DOT
# It shouldn't wrap hexadecimal, binary and octal literals
and not value.startswith(("0x", "0b", "0o"))
# It shouldn't wrap complex literals
and "j" not in value
):
wrap_in_parentheses(node, leaf)

yield from self.visit_default(node)

def visit_SEMI(self, leaf: Leaf) -> Iterator[Line]:
"""Remove a semicolon and put the other statement on a separate line."""
yield from self.line()
Expand Down
7 changes: 1 addition & 6 deletions src/black/nodes.py
Expand Up @@ -306,12 +306,7 @@ def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
return NO

if not prev:
if t == token.DOT:
prevp = preceding_leaf(p)
if not prevp or prevp.type != token.NUMBER:
return NO

elif t == token.LSQB:
if t == token.DOT or t == token.LSQB:
return NO

elif prev.type != token.COMMA:
Expand Down
47 changes: 47 additions & 0 deletions tests/data/attribute_access_on_number_literals.py
@@ -0,0 +1,47 @@
x = 123456789 .bit_count()
x = (123456).__abs__()
x = .1.is_integer()
x = 1. .imag
x = 1E+1.imag
x = 1E-1.real
x = 123456789.123456789.hex()
x = 123456789.123456789E123456789 .real
x = 123456789E123456789 .conjugate()
x = 123456789J.real
x = 123456789.123456789J.__add__(0b1011.bit_length())
x = 0XB1ACC.conjugate()
x = 0B1011 .conjugate()
x = 0O777 .real
x = 0.000000006 .hex()
x = -100.0000J

if 10 .real:
...

y = 100[no]
y = 100(no)

# output

x = (123456789).bit_count()
x = (123456).__abs__()
x = (0.1).is_integer()
x = (1.0).imag
x = (1e1).imag
x = (1e-1).real
x = (123456789.123456789).hex()
x = (123456789.123456789e123456789).real
x = (123456789e123456789).conjugate()
x = 123456789j.real
x = 123456789.123456789j.__add__(0b1011.bit_length())
x = 0xB1ACC.conjugate()
x = 0b1011.conjugate()
x = 0o777.real
x = (0.000000006).hex()
x = -100.0000j

if (10).real:
...

y = 100[no]
y = 100(no)
9 changes: 6 additions & 3 deletions tests/data/expression.diff
Expand Up @@ -11,7 +11,7 @@
True
False
1
@@ -21,71 +21,104 @@
@@ -21,99 +21,135 @@
Name1 or (Name2 and Name3) or Name4
Name1 or Name2 and Name3 or Name4
v1 << 2
Expand Down Expand Up @@ -144,8 +144,11 @@
call(**self.screen_kwargs)
call(b, **self.screen_kwargs)
lukasz.langa.pl
@@ -94,26 +127,29 @@
1.0 .real
call.me(maybe)
-1 .real
-1.0 .real
+(1).real
+(1.0).real
....__class__
list[str]
dict[str, int]
Expand Down
4 changes: 2 additions & 2 deletions tests/data/expression.py
Expand Up @@ -382,8 +382,8 @@ async def f():
call(b, **self.screen_kwargs)
lukasz.langa.pl
call.me(maybe)
1 .real
1.0 .real
(1).real
(1.0).real
....__class__
list[str]
dict[str, int]
Expand Down
9 changes: 6 additions & 3 deletions tests/data/expression_skip_magic_trailing_comma.diff
Expand Up @@ -11,7 +11,7 @@
True
False
1
@@ -21,71 +21,92 @@
@@ -21,99 +21,118 @@
Name1 or (Name2 and Name3) or Name4
Name1 or Name2 and Name3 or Name4
v1 << 2
Expand Down Expand Up @@ -132,8 +132,11 @@
call(**self.screen_kwargs)
call(b, **self.screen_kwargs)
lukasz.langa.pl
@@ -94,26 +115,24 @@
1.0 .real
call.me(maybe)
-1 .real
-1.0 .real
+(1).real
+(1.0).real
....__class__
list[str]
dict[str, int]
Expand Down
3 changes: 3 additions & 0 deletions tests/data/tricky_unicode_symbols.py
Expand Up @@ -4,3 +4,6 @@
x󠄀 = 4
មុ = 1
Q̇_per_meter = 4

A᧚ = 3
A፩ = 8
1 change: 1 addition & 0 deletions tests/test_format.py
Expand Up @@ -15,6 +15,7 @@
)

SIMPLE_CASES: List[str] = [
"attribute_access_on_number_literals",
"beginning_backslash",
"bracketmatch",
"class_blank_parentheses",
Expand Down

0 comments on commit 69e10a8

Please sign in to comment.