Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4380e55

Browse files
ranjodhsingh1729Pedro-Muller29pre-commit-ci[bot]cobaltt7
authored
Standardize type comments to form # type: (value) (psf#2097) (psf#4645)
* Standardize type comments to form `# type: (value)` (psf#2097) Co-authored-by: Pedro Mezacasa Muller <[email protected]> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Change the missed `NON_BREAKING_SPACE` to `"\N{NO-BREAK SPACE}"` per RUF001 (PR psf#4694); * Clean up unnecessary strip calls as per review * Just triggering CI with something trivial * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add the missing newline at the end of the test case --------- Co-authored-by: Pedro Mezacasa Muller <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: cobalt <[email protected]>
1 parent 3a9b57f commit 4380e55

9 files changed

Lines changed: 105 additions & 31 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- Fix bug where module docstrings would be treated as normal strings if preceeded by
1818
comments (#4764)
1919
- Fix bug where python 3.12 generics syntax split line happens weirdly (#4777)
20+
- Standardize type comments to form `# type: <value>` (#4645)
2021

2122
### Configuration
2223

docs/the_black_code_style/future_style.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Currently, the following features are included in the preview style:
2929
- `fix_fmt_skip_in_one_liners`: Fix `# fmt: skip` behaviour on one-liner declarations,
3030
such as `def foo(): return "mock" # fmt: skip`, where previously the declaration would
3131
have been incorrectly collapsed.
32+
- `standardize_type_comments`: Format type comments which have zero or more spaces
33+
between `#` and `type:` or between `type:` and value to `# type: (value)`
3234
- `wrap_comprehension_in`: Wrap the `in` clause of list and dictionary comprehensions
3335
across lines if it would otherwise exceed the maximum line length.
3436
- `remove_parens_around_except_types`: Remove parentheses around multiple exception

src/black/comments.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
WHITESPACE,
1212
container_of,
1313
first_leaf_of,
14+
is_type_comment_string,
1415
make_simple_prefix,
1516
preceding_leaf,
1617
syms,
@@ -50,7 +51,7 @@ class ProtoComment:
5051
leading_whitespace: str # leading whitespace before the comment, if any
5152

5253

53-
def generate_comments(leaf: LN) -> Iterator[Leaf]:
54+
def generate_comments(leaf: LN, mode: Mode) -> Iterator[Leaf]:
5455
"""Clean the prefix of the `leaf` and generate comments from it, if any.
5556
5657
Comments in lib2to3 are shoved into the whitespace prefix. This happens
@@ -70,15 +71,17 @@ def generate_comments(leaf: LN) -> Iterator[Leaf]:
7071
are emitted with a fake STANDALONE_COMMENT token identifier.
7172
"""
7273
total_consumed = 0
73-
for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER):
74+
for pc in list_comments(
75+
leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER, mode=mode
76+
):
7477
total_consumed = pc.consumed
7578
prefix = make_simple_prefix(pc.newlines, pc.form_feed)
7679
yield Leaf(pc.type, pc.value, prefix=prefix)
7780
normalize_trailing_prefix(leaf, total_consumed)
7881

7982

8083
@lru_cache(maxsize=4096)
81-
def list_comments(prefix: str, *, is_endmarker: bool) -> list[ProtoComment]:
84+
def list_comments(prefix: str, *, is_endmarker: bool, mode: Mode) -> list[ProtoComment]:
8285
"""Return a list of :class:`ProtoComment` objects parsed from the given `prefix`."""
8386
result: list[ProtoComment] = []
8487
if not prefix or "#" not in prefix:
@@ -109,7 +112,7 @@ def list_comments(prefix: str, *, is_endmarker: bool) -> list[ProtoComment]:
109112
comment_type = token.COMMENT # simple trailing comment
110113
else:
111114
comment_type = STANDALONE_COMMENT
112-
comment = make_comment(line)
115+
comment = make_comment(line, mode=mode)
113116
result.append(
114117
ProtoComment(
115118
type=comment_type,
@@ -140,7 +143,7 @@ def normalize_trailing_prefix(leaf: LN, total_consumed: int) -> None:
140143
leaf.prefix = ""
141144

142145

143-
def make_comment(content: str) -> str:
146+
def make_comment(content: str, mode: Mode) -> str:
144147
"""Return a consistently formatted comment from the given `content` string.
145148
146149
All comments (except for "##", "#!", "#:", '#'") should have a single
@@ -157,9 +160,18 @@ def make_comment(content: str) -> str:
157160
if (
158161
content
159162
and content[0] == "\N{NO-BREAK SPACE}"
160-
and not content.lstrip().startswith("type:")
163+
and not is_type_comment_string("# " + content.lstrip(), mode=mode)
161164
):
162165
content = " " + content[1:] # Replace NBSP by a simple space
166+
if (
167+
Preview.standardize_type_comments in mode
168+
and content
169+
and "\N{NO-BREAK SPACE}" not in content
170+
and is_type_comment_string("#" + content, mode=mode)
171+
):
172+
type_part, value_part = content.split(":", 1)
173+
content = type_part.strip() + ": " + value_part.strip()
174+
163175
if content and content[0] not in COMMENT_EXCEPTIONS:
164176
content = " " + content
165177
return "#" + content
@@ -183,7 +195,7 @@ def convert_one_fmt_off_pair(
183195
"""
184196
for leaf in node.leaves():
185197
previous_consumed = 0
186-
for comment in list_comments(leaf.prefix, is_endmarker=False):
198+
for comment in list_comments(leaf.prefix, is_endmarker=False, mode=mode):
187199
is_fmt_off = comment.value in FMT_OFF
188200
is_fmt_skip = _contains_fmt_skip_comment(comment.value, mode)
189201
if (not is_fmt_off and not is_fmt_skip) or (
@@ -273,13 +285,13 @@ def generate_ignored_nodes(
273285
return
274286
container: Optional[LN] = container_of(leaf)
275287
while container is not None and container.type != token.ENDMARKER:
276-
if is_fmt_on(container):
288+
if is_fmt_on(container, mode=mode):
277289
return
278290

279291
# fix for fmt: on in children
280-
if children_contains_fmt_on(container):
292+
if children_contains_fmt_on(container, mode=mode):
281293
for index, child in enumerate(container.children):
282-
if isinstance(child, Leaf) and is_fmt_on(child):
294+
if isinstance(child, Leaf) and is_fmt_on(child, mode=mode):
283295
if child.type in CLOSING_BRACKETS:
284296
# This means `# fmt: on` is placed at a different bracket level
285297
# than `# fmt: off`. This is an invalid use, but as a courtesy,
@@ -290,12 +302,14 @@ def generate_ignored_nodes(
290302
if (
291303
child.type == token.INDENT
292304
and index < len(container.children) - 1
293-
and children_contains_fmt_on(container.children[index + 1])
305+
and children_contains_fmt_on(
306+
container.children[index + 1], mode=mode
307+
)
294308
):
295309
# This means `# fmt: on` is placed right after an indentation
296310
# level, and we shouldn't swallow the previous INDENT token.
297311
return
298-
if children_contains_fmt_on(child):
312+
if children_contains_fmt_on(child, mode=mode):
299313
return
300314
yield child
301315
else:
@@ -316,7 +330,7 @@ def _generate_ignored_nodes_from_fmt_skip(
316330
ignored_nodes: list[LN] = []
317331
# Need to properly format the leaf prefix to compare it to comment.value,
318332
# which is also formatted
319-
comments = list_comments(leaf.prefix, is_endmarker=False)
333+
comments = list_comments(leaf.prefix, is_endmarker=False, mode=mode)
320334
if not comments or comment.value != comments[0].value:
321335
return
322336
if prev_sibling is not None:
@@ -392,24 +406,24 @@ def _generate_ignored_nodes_from_fmt_skip(
392406
yield from iter(ignored_nodes)
393407

394408

395-
def is_fmt_on(container: LN) -> bool:
409+
def is_fmt_on(container: LN, mode: Mode) -> bool:
396410
"""Determine whether formatting is switched on within a container.
397411
Determined by whether the last `# fmt:` comment is `on` or `off`.
398412
"""
399413
fmt_on = False
400-
for comment in list_comments(container.prefix, is_endmarker=False):
414+
for comment in list_comments(container.prefix, is_endmarker=False, mode=mode):
401415
if comment.value in FMT_ON:
402416
fmt_on = True
403417
elif comment.value in FMT_OFF:
404418
fmt_on = False
405419
return fmt_on
406420

407421

408-
def children_contains_fmt_on(container: LN) -> bool:
422+
def children_contains_fmt_on(container: LN, mode: Mode) -> bool:
409423
"""Determine if children have formatting switched on."""
410424
for child in container.children:
411425
leaf = first_leaf_of(child)
412-
if leaf is not None and is_fmt_on(leaf):
426+
if leaf is not None and is_fmt_on(leaf, mode=mode):
413427
return True
414428

415429
return False

src/black/linegen.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def visit_default(self, node: LN) -> Iterator[Line]:
140140
"""Default `visit_*()` implementation. Recurses to children of `node`."""
141141
if isinstance(node, Leaf):
142142
any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
143-
for comment in generate_comments(node):
143+
for comment in generate_comments(node, mode=self.mode):
144144
if any_open_brackets:
145145
# any comment within brackets is subject to splitting
146146
self.current_line.append(comment)
@@ -1420,7 +1420,7 @@ def normalize_invisible_parens( # noqa: C901
14201420
Standardizes on visible parentheses for single-element tuples, and keeps
14211421
existing visible parentheses for other tuples and generator expressions.
14221422
"""
1423-
for pc in list_comments(node.prefix, is_endmarker=False):
1423+
for pc in list_comments(node.prefix, is_endmarker=False, mode=mode):
14241424
if pc.value in FMT_OFF:
14251425
# This `node` has a prefix with `# fmt: off`, don't mess with parens.
14261426
return
@@ -1748,7 +1748,7 @@ def maybe_make_parens_invisible_in_atom(
17481748
if (
17491749
# If the prefix of `middle` includes a type comment with
17501750
# ignore annotation, then we do not remove the parentheses
1751-
not is_type_ignore_comment_string(middle.prefix.strip())
1751+
not is_type_ignore_comment_string(middle.prefix.strip(), mode=mode)
17521752
):
17531753
first.value = ""
17541754
last.value = ""

src/black/lines.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,9 @@ def contains_uncollapsable_type_comments(self) -> bool:
286286
comment_seen = False
287287
for leaf_id, comments in self.comments.items():
288288
for comment in comments:
289-
if is_type_comment(comment):
289+
if is_type_comment(comment, mode=self.mode):
290290
if comment_seen or (
291-
not is_type_ignore_comment(comment)
291+
not is_type_ignore_comment(comment, mode=self.mode)
292292
and leaf_id not in ignored_ids
293293
):
294294
return True
@@ -325,7 +325,7 @@ def contains_unsplittable_type_ignore(self) -> bool:
325325
# line.
326326
for node in self.leaves[-2:]:
327327
for comment in self.comments.get(id(node), []):
328-
if is_type_ignore_comment(comment):
328+
if is_type_ignore_comment(comment, mode=self.mode):
329329
return True
330330

331331
return False
@@ -400,7 +400,7 @@ def append_comment(self, comment: Leaf) -> bool:
400400
and not last_leaf.value
401401
and last_leaf.parent
402402
and len(list(last_leaf.parent.leaves())) <= 3
403-
and not is_type_comment(comment)
403+
and not is_type_comment(comment, mode=self.mode)
404404
):
405405
# Comments on an optional parens wrapping a single leaf should belong to
406406
# the wrapped node except if it's a type comment. Pinning the comment like

src/black/mode.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ class Preview(Enum):
231231
multiline_string_handling = auto()
232232
always_one_newline_after_import = auto()
233233
fix_fmt_skip_in_one_liners = auto()
234+
standardize_type_comments = auto()
234235
wrap_comprehension_in = auto()
235236
# Remove parentheses around multiple exception types in except and
236237
# except* without as. See PEP 758 for details.
@@ -319,3 +320,18 @@ def get_cache_key(self) -> str:
319320
features_and_magics,
320321
]
321322
return ".".join(parts)
323+
324+
def __hash__(self) -> int:
325+
return hash((
326+
frozenset(self.target_versions),
327+
self.line_length,
328+
self.string_normalization,
329+
self.is_pyi,
330+
self.is_ipynb,
331+
self.skip_source_first_line,
332+
self.magic_trailing_comma,
333+
frozenset(self.python_cell_magics),
334+
self.preview,
335+
self.unstable,
336+
frozenset(self.enabled_features),
337+
))

src/black/nodes.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from mypy_extensions import mypyc_attr
1515

1616
from black.cache import CACHE_DIR
17-
from black.mode import Mode
17+
from black.mode import Mode, Preview
1818
from black.strings import get_string_prefix, has_triple_quotes
1919
from blib2to3 import pygram
2020
from blib2to3.pgen2 import token
@@ -931,27 +931,44 @@ def is_async_stmt_or_funcdef(leaf: Leaf) -> bool:
931931
)
932932

933933

934-
def is_type_comment(leaf: Leaf) -> bool:
934+
def is_type_comment(leaf: Leaf, mode: Mode) -> bool:
935935
"""Return True if the given leaf is a type comment. This function should only
936936
be used for general type comments (excluding ignore annotations, which should
937937
use `is_type_ignore_comment`). Note that general type comments are no longer
938938
used in modern version of Python, this function may be deprecated in the future."""
939939
t = leaf.type
940940
v = leaf.value
941-
return t in {token.COMMENT, STANDALONE_COMMENT} and v.startswith("# type:")
941+
return t in {token.COMMENT, STANDALONE_COMMENT} and is_type_comment_string(v, mode)
942942

943943

944-
def is_type_ignore_comment(leaf: Leaf) -> bool:
944+
def is_type_comment_string(value: str, mode: Mode) -> bool:
945+
if Preview.standardize_type_comments in mode:
946+
is_valid = value.startswith("#") and value[1:].lstrip().startswith("type:")
947+
else:
948+
is_valid = value.startswith("# type:")
949+
return is_valid
950+
951+
952+
def is_type_ignore_comment(leaf: Leaf, mode: Mode) -> bool:
945953
"""Return True if the given leaf is a type comment with ignore annotation."""
946954
t = leaf.type
947955
v = leaf.value
948-
return t in {token.COMMENT, STANDALONE_COMMENT} and is_type_ignore_comment_string(v)
956+
return t in {token.COMMENT, STANDALONE_COMMENT} and is_type_ignore_comment_string(
957+
v, mode
958+
)
949959

950960

951-
def is_type_ignore_comment_string(value: str) -> bool:
961+
def is_type_ignore_comment_string(value: str, mode: Mode) -> bool:
952962
"""Return True if the given string match with type comment with
953963
ignore annotation."""
954-
return value.startswith("# type: ignore")
964+
if Preview.standardize_type_comments in mode:
965+
is_valid = is_type_comment_string(value, mode) and value.split(":", 1)[
966+
1
967+
].lstrip().startswith("ignore")
968+
else:
969+
is_valid = value.startswith("# type: ignore")
970+
971+
return is_valid
955972

956973

957974
def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None:

src/black/resources/black.schema.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"multiline_string_handling",
8787
"always_one_newline_after_import",
8888
"fix_fmt_skip_in_one_liners",
89+
"standardize_type_comments",
8990
"wrap_comprehension_in",
9091
"remove_parens_around_except_types",
9192
"normalize_cr_newlines",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# flags: --preview
2+
def foo(
3+
a, #type:int
4+
b, #type: str
5+
c, # type: List[int]
6+
d, # type: Dict[int, str]
7+
e, # type: ignore
8+
f, # type : ignore
9+
g, # type : ignore
10+
):
11+
pass
12+
13+
# output
14+
def foo(
15+
a, # type: int
16+
b, # type: str
17+
c, # type: List[int]
18+
d, # type: Dict[int, str]
19+
e, # type: ignore
20+
f, # type : ignore
21+
g, # type : ignore
22+
):
23+
pass

0 commit comments

Comments
 (0)