Skip to content
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

Merged
merged 4 commits into from
May 1, 2018
Merged

Format subscriptions in a PEP-8 compliant way #178

merged 4 commits into from
May 1, 2018

Conversation

zsol
Copy link
Collaborator

@zsol zsol commented Apr 29, 2018

Fixes #157

@ambv
Copy link
Collaborator

ambv commented Apr 29, 2018

Install pre-commit, fix mypy, rest looks good 👍🏻

@zsol
Copy link
Collaborator Author

zsol commented Apr 29, 2018

Looks like we'll need to disable E203

@ambv
Copy link
Collaborator

ambv commented Apr 29, 2018

E203 is spaces before commas. Where do we need to do that?

@zsol
Copy link
Collaborator Author

zsol commented Apr 29, 2018

There's an E203 on this line: https://github.com/ambv/black/blob/1c8a6200c03e8452dde91aeda12f0c6f0738f8dd/black.py#L2104

black.py:2111:56: E203 whitespace before ':'

@zsol
Copy link
Collaborator Author

zsol commented Apr 29, 2018

this seems relevant PyCQA/pycodestyle#373

@ambv
Copy link
Collaborator

ambv commented Apr 29, 2018

Welp, OK, then the rule is stupid. We need to add it to docs as one of the stupid rules (next to W503).

@coveralls
Copy link

coveralls commented Apr 29, 2018

Pull Request Test Coverage Report for Build 310

  • 38 of 38 (100.0%) changed or added relevant lines in 2 files are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage increased (+0.02%) to 94.881%

Files with Coverage Reduction New Missed Lines %
black.py 1 93.82%
Totals Coverage Status
Change from base Build 297: 0.02%
Covered Lines: 1798
Relevant Lines: 1895

💛 - Coveralls

Copy link
Collaborator

@ambv ambv left a 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())
)
Copy link
Collaborator

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]
Copy link
Collaborator

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(
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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.

Copy link
Collaborator Author

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 ifs 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"
Copy link
Collaborator

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 ``:``
Copy link
Collaborator

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?

Copy link
Collaborator Author

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 ``:``
Copy link
Collaborator

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 = {
Copy link
Collaborator

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 tests. 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.
Copy link
Collaborator

@ambv ambv left a 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)."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Adding a leaf argument to get_open_lsqb() creates a rather bizarre signature. It suggests that leaf's position on the line has something to do with the result but it doesn't. It's simply a special case if leaf itself is a LSQB and is not otherwise used. I think that path should be handled directly in is_complex_subscript().

  2. 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(
Copy link
Collaborator

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."""
Copy link
Collaborator

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:
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants