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

Skip to content

Commit f87df0e

Browse files
authored
dont skip formatting #%% (psf#2919)
Fixes psf#2588
1 parent fa7f015 commit f87df0e

6 files changed

Lines changed: 57 additions & 27 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
<!-- Changes that affect Black's preview style -->
1616

17+
- Code cell separators `#%%` are now standardised to `# %%` (#2919)
18+
1719
### _Blackd_
1820

1921
<!-- Changes to blackd -->

src/black/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,7 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str:
11661166
else:
11671167
versions = detect_target_versions(src_node, future_imports=future_imports)
11681168

1169-
normalize_fmt_off(src_node)
1169+
normalize_fmt_off(src_node, preview=mode.preview)
11701170
lines = LineGenerator(mode=mode)
11711171
elt = EmptyLineTracker(is_pyi=mode.is_pyi)
11721172
empty_line = Line(mode=mode)

src/black/comments.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
FMT_PASS: Final = {*FMT_OFF, *FMT_SKIP}
2424
FMT_ON: Final = {"# fmt: on", "# fmt:on", "# yapf: enable"}
2525

26+
COMMENT_EXCEPTIONS = {True: " !:#'", False: " !:#'%"}
27+
2628

2729
@dataclass
2830
class ProtoComment:
@@ -42,7 +44,7 @@ class ProtoComment:
4244
consumed: int # how many characters of the original leaf's prefix did we consume
4345

4446

45-
def generate_comments(leaf: LN) -> Iterator[Leaf]:
47+
def generate_comments(leaf: LN, *, preview: bool) -> Iterator[Leaf]:
4648
"""Clean the prefix of the `leaf` and generate comments from it, if any.
4749
4850
Comments in lib2to3 are shoved into the whitespace prefix. This happens
@@ -61,12 +63,16 @@ def generate_comments(leaf: LN) -> Iterator[Leaf]:
6163
Inline comments are emitted as regular token.COMMENT leaves. Standalone
6264
are emitted with a fake STANDALONE_COMMENT token identifier.
6365
"""
64-
for pc in list_comments(leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER):
66+
for pc in list_comments(
67+
leaf.prefix, is_endmarker=leaf.type == token.ENDMARKER, preview=preview
68+
):
6569
yield Leaf(pc.type, pc.value, prefix="\n" * pc.newlines)
6670

6771

6872
@lru_cache(maxsize=4096)
69-
def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
73+
def list_comments(
74+
prefix: str, *, is_endmarker: bool, preview: bool
75+
) -> List[ProtoComment]:
7076
"""Return a list of :class:`ProtoComment` objects parsed from the given `prefix`."""
7177
result: List[ProtoComment] = []
7278
if not prefix or "#" not in prefix:
@@ -92,7 +98,7 @@ def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
9298
comment_type = token.COMMENT # simple trailing comment
9399
else:
94100
comment_type = STANDALONE_COMMENT
95-
comment = make_comment(line)
101+
comment = make_comment(line, preview=preview)
96102
result.append(
97103
ProtoComment(
98104
type=comment_type, value=comment, newlines=nlines, consumed=consumed
@@ -102,10 +108,10 @@ def list_comments(prefix: str, *, is_endmarker: bool) -> List[ProtoComment]:
102108
return result
103109

104110

105-
def make_comment(content: str) -> str:
111+
def make_comment(content: str, *, preview: bool) -> str:
106112
"""Return a consistently formatted comment from the given `content` string.
107113
108-
All comments (except for "##", "#!", "#:", '#'", "#%%") should have a single
114+
All comments (except for "##", "#!", "#:", '#'") should have a single
109115
space between the hash sign and the content.
110116
111117
If `content` didn't start with a hash sign, one is provided.
@@ -123,26 +129,26 @@ def make_comment(content: str) -> str:
123129
and not content.lstrip().startswith("type:")
124130
):
125131
content = " " + content[1:] # Replace NBSP by a simple space
126-
if content and content[0] not in " !:#'%":
132+
if content and content[0] not in COMMENT_EXCEPTIONS[preview]:
127133
content = " " + content
128134
return "#" + content
129135

130136

131-
def normalize_fmt_off(node: Node) -> None:
137+
def normalize_fmt_off(node: Node, *, preview: bool) -> None:
132138
"""Convert content between `# fmt: off`/`# fmt: on` into standalone comments."""
133139
try_again = True
134140
while try_again:
135-
try_again = convert_one_fmt_off_pair(node)
141+
try_again = convert_one_fmt_off_pair(node, preview=preview)
136142

137143

138-
def convert_one_fmt_off_pair(node: Node) -> bool:
144+
def convert_one_fmt_off_pair(node: Node, *, preview: bool) -> bool:
139145
"""Convert content of a single `# fmt: off`/`# fmt: on` into a standalone comment.
140146
141147
Returns True if a pair was converted.
142148
"""
143149
for leaf in node.leaves():
144150
previous_consumed = 0
145-
for comment in list_comments(leaf.prefix, is_endmarker=False):
151+
for comment in list_comments(leaf.prefix, is_endmarker=False, preview=preview):
146152
if comment.value not in FMT_PASS:
147153
previous_consumed = comment.consumed
148154
continue
@@ -157,7 +163,7 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
157163
if comment.value in FMT_SKIP and prev.type in WHITESPACE:
158164
continue
159165

160-
ignored_nodes = list(generate_ignored_nodes(leaf, comment))
166+
ignored_nodes = list(generate_ignored_nodes(leaf, comment, preview=preview))
161167
if not ignored_nodes:
162168
continue
163169

@@ -197,7 +203,9 @@ def convert_one_fmt_off_pair(node: Node) -> bool:
197203
return False
198204

199205

200-
def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
206+
def generate_ignored_nodes(
207+
leaf: Leaf, comment: ProtoComment, *, preview: bool
208+
) -> Iterator[LN]:
201209
"""Starting from the container of `leaf`, generate all leaves until `# fmt: on`.
202210
203211
If comment is skip, returns leaf only.
@@ -221,34 +229,34 @@ def generate_ignored_nodes(leaf: Leaf, comment: ProtoComment) -> Iterator[LN]:
221229
yield leaf.parent
222230
return
223231
while container is not None and container.type != token.ENDMARKER:
224-
if is_fmt_on(container):
232+
if is_fmt_on(container, preview=preview):
225233
return
226234

227235
# fix for fmt: on in children
228-
if contains_fmt_on_at_column(container, leaf.column):
236+
if contains_fmt_on_at_column(container, leaf.column, preview=preview):
229237
for child in container.children:
230-
if contains_fmt_on_at_column(child, leaf.column):
238+
if contains_fmt_on_at_column(child, leaf.column, preview=preview):
231239
return
232240
yield child
233241
else:
234242
yield container
235243
container = container.next_sibling
236244

237245

238-
def is_fmt_on(container: LN) -> bool:
246+
def is_fmt_on(container: LN, preview: bool) -> bool:
239247
"""Determine whether formatting is switched on within a container.
240248
Determined by whether the last `# fmt:` comment is `on` or `off`.
241249
"""
242250
fmt_on = False
243-
for comment in list_comments(container.prefix, is_endmarker=False):
251+
for comment in list_comments(container.prefix, is_endmarker=False, preview=preview):
244252
if comment.value in FMT_ON:
245253
fmt_on = True
246254
elif comment.value in FMT_OFF:
247255
fmt_on = False
248256
return fmt_on
249257

250258

251-
def contains_fmt_on_at_column(container: LN, column: int) -> bool:
259+
def contains_fmt_on_at_column(container: LN, column: int, *, preview: bool) -> bool:
252260
"""Determine if children at a given column have formatting switched on."""
253261
for child in container.children:
254262
if (
@@ -257,7 +265,7 @@ def contains_fmt_on_at_column(container: LN, column: int) -> bool:
257265
or isinstance(child, Leaf)
258266
and child.column == column
259267
):
260-
if is_fmt_on(child):
268+
if is_fmt_on(child, preview=preview):
261269
return True
262270

263271
return False

src/black/linegen.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def visit_default(self, node: LN) -> Iterator[Line]:
7272
"""Default `visit_*()` implementation. Recurses to children of `node`."""
7373
if isinstance(node, Leaf):
7474
any_open_brackets = self.current_line.bracket_tracker.any_open_brackets()
75-
for comment in generate_comments(node):
75+
for comment in generate_comments(node, preview=self.mode.preview):
7676
if any_open_brackets:
7777
# any comment within brackets is subject to splitting
7878
self.current_line.append(comment)
@@ -132,7 +132,7 @@ def visit_stmt(
132132
`parens` holds a set of string leaf values immediately after which
133133
invisible parens should be put.
134134
"""
135-
normalize_invisible_parens(node, parens_after=parens)
135+
normalize_invisible_parens(node, parens_after=parens, preview=self.mode.preview)
136136
for child in node.children:
137137
if is_name_token(child) and child.value in keywords:
138138
yield from self.line()
@@ -141,7 +141,7 @@ def visit_stmt(
141141

142142
def visit_match_case(self, node: Node) -> Iterator[Line]:
143143
"""Visit either a match or case statement."""
144-
normalize_invisible_parens(node, parens_after=set())
144+
normalize_invisible_parens(node, parens_after=set(), preview=self.mode.preview)
145145

146146
yield from self.line()
147147
for child in node.children:
@@ -802,7 +802,9 @@ def normalize_prefix(leaf: Leaf, *, inside_brackets: bool) -> None:
802802
leaf.prefix = ""
803803

804804

805-
def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
805+
def normalize_invisible_parens(
806+
node: Node, parens_after: Set[str], *, preview: bool
807+
) -> None:
806808
"""Make existing optional parentheses invisible or create new ones.
807809
808810
`parens_after` is a set of string leaf values immediately after which parens
@@ -811,7 +813,7 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
811813
Standardizes on visible parentheses for single-element tuples, and keeps
812814
existing visible parentheses for other tuples and generator expressions.
813815
"""
814-
for pc in list_comments(node.prefix, is_endmarker=False):
816+
for pc in list_comments(node.prefix, is_endmarker=False, preview=preview):
815817
if pc.value in FMT_OFF:
816818
# This `node` has a prefix with `# fmt: off`, don't mess with parens.
817819
return
@@ -820,7 +822,9 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
820822
# Fixes a bug where invisible parens are not properly stripped from
821823
# assignment statements that contain type annotations.
822824
if isinstance(child, Node) and child.type == syms.annassign:
823-
normalize_invisible_parens(child, parens_after=parens_after)
825+
normalize_invisible_parens(
826+
child, parens_after=parens_after, preview=preview
827+
)
824828

825829
# Add parentheses around long tuple unpacking in assignments.
826830
if (

tests/data/comments8.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# The percent-percent comments are Spyder IDE cells.
2+
# Both `#%%`` and `# %%` are accepted, so `black` standardises
3+
# to the latter.
4+
5+
#%%
6+
# %%
7+
8+
# output
9+
10+
# The percent-percent comments are Spyder IDE cells.
11+
# Both `#%%`` and `# %%` are accepted, so `black` standardises
12+
# to the latter.
13+
14+
# %%
15+
# %%

tests/test_format.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
# string processing
7676
"cantfit",
7777
"comments7",
78+
"comments8",
7879
"long_strings",
7980
"long_strings__edge_case",
8081
"long_strings__regression",

0 commit comments

Comments
 (0)