* fix indentation of line breaks in long type hints by adding parentheses, and remove unnecessary parentheses
* add entry in CHANGES.md, make the style change only in preview mode
The idea behind this change is that we stop looking into previous body to determine if there should be a blank before a function or class definition.
Input:
```python
import sys
if sys.version_info > (3, 7):
class Nested1:
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
class Nested2:
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
if sys.version_info > (3, 7):
def nested1():
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
def nested2():
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
```
Stable style
```python
import sys
if sys.version_info > (3, 7):
class Nested1:
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
class Nested2:
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
if sys.version_info > (3, 7):
def nested1():
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
def nested2():
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
```
In the stable formatting, we have a blank line sometimes, not depending on the previous statement on the same level, but on the last (potentially nested) statement in the previous body.
#2783/#3564 fixes this for classes in preview style:
```python
import sys
if sys.version_info > (3, 7):
class Nested1:
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
class Nested2:
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
if sys.version_info > (3, 7):
def nested1():
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
def nested2():
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
```
This PR additionally fixes this for function definitions:
```python
if sys.version_info > (3, 7):
if sys.platform == "win32":
assignment = 1
def function_definition(self): ...
def f1(self) -> str: ...
if sys.platform != "win32":
def function_definition(self): ...
assignment = 1
def f2(self) -> str: ...
if sys.version_info > (3, 8):
if sys.platform == "win32":
assignment = 1
def function_definition(self): ...
class F1: ...
if sys.platform != "win32":
def function_definition(self): ...
assignment = 1
class F2: ...
```
You can see the effect of this change on typeshed in https://github.com/konstin/typeshed/pull/1/files. As baseline, the preview mode changes without this PR are at https://github.com/konstin/typeshed/pull/2.
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
`is_type_comment` now specifically deals with general type comments for a leaf.
`is_type_ignore_comment` now handles type comments contains ignore annotation for a leaf
`is_type_ignore_comment_string` used to determine if a string is an ignore type comment
IPython is a very expensive import, like, at least 300ms. I'd also
venture that it's much more common than tokenize-rt, which is like 30ms.
I work in a repo where I use black, have IPython installed and there
happen to be a couple notebooks (that we don't want formatted). I know I
can force exclude ipynb, but this change doesn't really have a cost.
Currently the verbose logging for "Sources to be formatted" is a little
suspect in that it is a completely different code path from
`get_sources`.
This can result in bugs like https://github.com/psf/black/pull/3216#issuecomment-1213557359
and generally limits the value of these logs.
This does change the "when" of this log, but the colours help separate
it from the even more verbose logs.
Avoids a Python 3.12 deprecation warning.
Subtle difference: previously, timestamps in diff filenames had the
`+0000` separated from the timestamp by space. With this, the space is
there no more, and there is a colon, as in `+00:00`.
This patch changes the preview style so that string splitters respect
Unicode East Asian Width[^1] property. If you are not familiar to CJK
languages it is not clear immediately. Let me elaborate with some
examples.
Traditionally, East Asian characters (including punctuation) have
taken up space twice than European letters and stops when they are
rendered in monospace typeset. Compare the following characters:
```
abcdefg.
글、字。
```
The characters at the first line are half-width, and the second line
are full-width. (Also note that the last character with a small
circle, the East Asian period, is also full-width.) Therefore, if we
want to prevent those full-width characters to exceed the maximum
columns per line, we need to count their *width* rather than the number
of characters. Again, the following characters:
```
글、字。
```
These are just 4 characters, but their total width is 8.
Suppose we want to maintain up to 4 columns per line with the following
text:
```
abcdefg.
글、字。
```
How should it be then? We want it to look like:
```
abcd
efg.
글、
字。
```
However, Black currently turns it into like this:
```
abcd
efg.
글、字。
```
It's because Black currently counts the number of characters in the line
instead of measuring their width. So, how could we measure the width?
How can we tell if a character is full- or half-width? What if half-width
characters and full-width ones are mixed in a line? That's why Unicode
defined an attribute named `East_Asian_Width`. Unicode grouped every
single character according to their width in fixed-width typeset.
This partially addresses #1197, but only for string splitters. The other
parts need to be fixed as well in future patches.
This was implemented by copying rich's own approach to handling wide
characters: generate a table using wcwidth, check it into source
control, and use in to drive helper functions in Black's logic. This
gets us the best of both worlds: accuracy and performance (and let's us
update as per our stability policy too!).
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>