-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Format subscriptions in a PEP-8 compliant way #178
Conversation
Install pre-commit, fix mypy, rest looks good 👍🏻 |
Looks like we'll need to disable |
E203 is spaces before commas. Where do we need to do that? |
There's an E203 on this line: https://github.com/ambv/black/blob/1c8a6200c03e8452dde91aeda12f0c6f0738f8dd/black.py#L2104
|
this seems relevant PyCQA/pycodestyle#373 |
Welp, OK, then the rule is stupid. We need to add it to docs as one of the stupid rules (next to W503). |
Pull Request Test Coverage Report for Build 310
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, that's a pretty complex patch. Thanks! Blocked on a few things, see comments. Nothing critical but better get it right the first time :-)
black.py
Outdated
subscript_start = child_towards(subscript_start, leaf) | ||
complex_subscript = subscript_start is not None and any( | ||
map(lambda n: n.type in EXPRS, subscript_start.pre_order()) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this be shorter as
complex_subscript = subscript_start is not None and any(n.type in EXPRS for n in subscript_start.pre_order())
black.py
Outdated
@@ -726,7 +736,26 @@ def append(self, leaf: Leaf, preformatted: bool = False) -> None: | |||
if self.leaves and not preformatted: | |||
# Note: at this point leaf.prefix should be empty except for | |||
# imports, for which we only preserve newlines. | |||
leaf.prefix += whitespace(leaf) | |||
lsqb_leaf: Optional[Leaf] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of this hunting for complex_subscript
needs to be its own method. I want append()
(and any other method, really) to remain very simple, almost pseudocode.
Call the method is_complex_subscript(self, leaf)
and then append()
becomes trivial again.
black.py
Outdated
if leaf.type == token.LSQB: | ||
lsqb_leaf = leaf | ||
else: | ||
lsqb_leaf = self.bracket_tracker.bracket_match.get( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I consider bracket_match
an implementation detail of the bracket_tracker
. Create a method like get_open_lsqb()
on BracketTracker that we can call directly. Then the code reads less magical.
black.py
Outdated
if prevp.type == token.COLON: | ||
return NO | ||
|
||
return SPACE if prevp.type == token.COMMA or complex_subscript else NO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the ternary expression stopped being trivial, it's clearner to just rip out another regular if
here. And reword it in a way that only returns NO (see review comment about SPACE below).
black.py
Outdated
@@ -1464,8 +1503,7 @@ def whitespace(leaf: Leaf) -> str: # noqa C901 | |||
|
|||
return NO | |||
|
|||
else: | |||
return NO | |||
return SPACE if complex_subscript else NO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead:
elif not complex_subscript:
return NO
@@ -1349,7 +1388,7 @@ def whitespace(leaf: Leaf) -> str: # noqa C901 | |||
|
|||
elif prevp.type == token.COLON: | |||
if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}: | |||
return NO | |||
return SPACE if complex_subscript else NO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default in this function is SPACE. We should avoid returning it from sub-branches.
Instead, just add another check to the if
on 1390.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to return SPACE
because otherwise control would flow to this block of if
s https://github.com/ambv/black/blob/8f6b5048fcb71bc59399fd7b5511d6e229033b4c/black.py#L1414
Unless you prefer to handle this case down there too
black.py
Outdated
complex_subscript = False | ||
if lsqb_leaf is not None: | ||
subscript_start = lsqb_leaf.next_sibling | ||
assert subscript_start is not None, "LSQB always has a right sibling" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check looks redundant to me. isinstance()
below guarantees it's not None for that branch, and subscript_start is not None
in complex_subscript =
guarantees it's not None later.
README.md
Outdated
PEP 8 [recommends](https://www.python.org/dev/peps/pep-0008/#whitespace-in-expressions-and-statements) | ||
to treat ``:`` in slices as a binary operator with the lowest priority, and to | ||
leave an equal amount of space on either side, except if a parameter is omitted | ||
(e.g. ``ham[1 + 1:]``). It also states that for extended slices, both ``:`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in the code we actually have lines like:
self.leaves[_opening_index + 1 :]
So I guess we are always adding a space for complex slices, including when there is just one slice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is a typo. Well spotted :)
README.md
Outdated
@@ -302,6 +302,19 @@ This behaviour may raise ``W503 line break before binary operator`` warnings in | |||
style guide enforcement tools like Flake8. Since ``W503`` is not PEP 8 compliant, | |||
you should tell Flake8 to ignore these warnings. | |||
|
|||
### Whiespace In Subscripts and ``:`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just ### Slices
would do the trick.
black.py
Outdated
@@ -582,6 +582,16 @@ def show(cls, code: str) -> None: | |||
syms.listmaker, | |||
syms.testlist_gexp, | |||
} | |||
EXPRS = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, what you actually wanted here is all children of syms.subscript
. If you look at Grammar.txt
, you'll see that those are test
s. So you're missing quite a few. I won't enumerate all here but try adding this to tests:
slice[lambda: None : lambda: None]
slice[1 or 2 : True and False]
slice[not so_simple : 1 < val <= 10]
Yeah, those are crazy but valid slices.
- Make `append` trivial again! - move lsqb hunting into `BracketTracker` - Consider expressions complex if they are children of `test` - README section rename and typo fix In two cases I still return `SPACE` explicitly in `whitespace()`. See comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some last nits to address.
black.py
Outdated
@@ -698,6 +715,13 @@ def maybe_decrement_after_lambda_arguments(self, leaf: Leaf) -> bool: | |||
|
|||
return False | |||
|
|||
def get_open_lsqb(self, leaf: Leaf) -> Optional[Leaf]: | |||
"""Returns the most recent opening square bracket at `leaf` (if any).""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
Adding a
leaf
argument toget_open_lsqb()
creates a rather bizarre signature. It suggests thatleaf
's position on the line has something to do with the result but it doesn't. It's simply a special case ifleaf
itself is a LSQB and is not otherwise used. I think that path should be handled directly inis_complex_subscript()
. -
nit: docstrings should be in imperative: "Return" rather than "Returns".
@@ -726,7 +750,9 @@ def append(self, leaf: Leaf, preformatted: bool = False) -> None: | |||
if self.leaves and not preformatted: | |||
# Note: at this point leaf.prefix should be empty except for | |||
# imports, for which we only preserve newlines. | |||
leaf.prefix += whitespace(leaf) | |||
leaf.prefix += whitespace( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, much better.
black.py
Outdated
@@ -920,6 +946,22 @@ def remove_trailing_comma(self) -> None: | |||
self.comments[i] = (comma_index - 1, comment) | |||
self.leaves.pop() | |||
|
|||
def is_complex_subscript(self, leaf: Leaf) -> bool: | |||
"""Returns True iff `leaf` is part of a slice with non-trivial exprs.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto about imperative.
black.py
Outdated
elif prevp.type != token.COMMA and not complex_subscript: | ||
return NO | ||
|
||
else: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better!
nit: else
is unnecessary
Fixes #157