From 86a51c54e4f753896b37b7d7d4021626b54d9b46 Mon Sep 17 00:00:00 2001 From: DesbyP Date: Fri, 28 Feb 2020 17:53:43 +0100 Subject: [PATCH 1/3] Fix false alarm in E203 rule. E203 checks for extraneous whitespace before a colon. Yet the colon can be used as a "slice" operator, and as such PEP8 asks for 0 or 1 whitespaces on both sides of the operator. --- pycodestyle.py | 31 ++++++++++++++++++++++++++++++- testsuite/E20.py | 6 ++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/pycodestyle.py b/pycodestyle.py index b7e0b2fb..8bdc738d 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -413,6 +413,12 @@ def extraneous_whitespace(logical_line): - Immediately inside parentheses, brackets or braces. - Immediately before a comma, semicolon, or colon. + Exceptions: + - When the colon acts as a slice, the rule of binary operators + applies and we should have the same amount of space on either side + - When the colon acts as a slice but a parameter is omitted, then + the space is omitted + Okay: spam(ham[1], {eggs: 2}) E201: spam( ham[1], {eggs: 2}) E201: spam(ham[ 1], {eggs: 2}) @@ -421,10 +427,32 @@ def extraneous_whitespace(logical_line): E202: spam(ham[1 ], {eggs: 2}) E202: spam(ham[1], {eggs: 2 }) + Okay: ham[8 : 2] + Okay: ham[: 2] E203: if x == 4: print x, y; x, y = y , x E203: if x == 4: print x, y ; x, y = y, x E203: if x == 4 : print x, y; x, y = y, x """ + + def is_a_slice(line, colon_position): + """Check colon acts as a slice + + Return True if the colon is directly contained within + square brackets. + """ + parentheses_brackets_braces = list() + for i in range(colon_position): + c = line[i] + if c in '[({': + parentheses_brackets_braces += c + elif c in '])}': + last_opened = parentheses_brackets_braces.pop() + expected_close = {'{': '}', '(': ')', '[': ']'}[last_opened] + if c != expected_close: # invalid Python code + return False + return (len(parentheses_brackets_braces) > 0 and + parentheses_brackets_braces.pop() == '[') + line = logical_line for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): text = match.group() @@ -433,7 +461,8 @@ def extraneous_whitespace(logical_line): if text == char + ' ': # assert char in '([{' yield found + 1, "E201 whitespace after '%s'" % char - elif line[found - 1] != ',': + elif (line[found - 1] != ',' and + not (char == ':' and is_a_slice(line, found))): code = ('E202' if char in '}])' else 'E203') # if char in ',;:' yield found, "%s whitespace before '%s'" % (code, char) diff --git a/testsuite/E20.py b/testsuite/E20.py index 2f1ecc28..1f9ad7ff 100644 --- a/testsuite/E20.py +++ b/testsuite/E20.py @@ -46,10 +46,16 @@ if x == 4: print x, y x, y = y , x +#: E203:1:37 +foo[idxs[2 : 6] : spam(ham[1], {eggs : a[2 : 4]})] #: Okay if x == 4: print x, y x, y = y, x a[b1, :] == a[b1, ...] b = a[:, b1] +ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:] +ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] +ham[lower + offset : upper + offset] +foo[idxs[2 : 6] : spam(ham[1], {eggs: a[2 : 4]})] #: From 7400affdb5db9be86a3adfc6301d8a0fb701afa4 Mon Sep 17 00:00:00 2001 From: DesbyP Date: Fri, 28 Feb 2020 19:37:10 +0100 Subject: [PATCH 2/3] Add support for nested lambda expression in brackets --- pycodestyle.py | 27 ++++++++++++++++++++------- testsuite/E20.py | 6 ++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/pycodestyle.py b/pycodestyle.py index 8bdc738d..b4452d9e 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -434,24 +434,37 @@ def extraneous_whitespace(logical_line): E203: if x == 4 : print x, y; x, y = y, x """ - def is_a_slice(line, colon_position): - """Check colon acts as a slice + def is_a_slice(line, space_pos): + """Check if the colon after an extra space acts as a slice Return True if the colon is directly contained within square brackets. """ + # list of found '[({' as tuples "(char, position)" parentheses_brackets_braces = list() - for i in range(colon_position): + for i in range(space_pos): c = line[i] if c in '[({': - parentheses_brackets_braces += c + # add it to the stack + parentheses_brackets_braces.append((c, i)) elif c in '])}': - last_opened = parentheses_brackets_braces.pop() + # unstack last item and check consistency + last_opened = parentheses_brackets_braces.pop()[0] expected_close = {'{': '}', '(': ')', '[': ']'}[last_opened] if c != expected_close: # invalid Python code return False - return (len(parentheses_brackets_braces) > 0 and - parentheses_brackets_braces.pop() == '[') + is_within_brackets = (len(parentheses_brackets_braces) > 0 and + parentheses_brackets_braces[-1][0] == '[') + + is_lambda_expr = False + if is_within_brackets: + # check for a lambda expression nested in brackets + if space_pos > 6: + last_opened = parentheses_brackets_braces[-1] + between_bracket_colon = line[last_opened[1] + 1 : space_pos] + is_lambda_expr = 'lambda' in between_bracket_colon + + return (is_within_brackets and not is_lambda_expr) line = logical_line for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line): diff --git a/testsuite/E20.py b/testsuite/E20.py index 1f9ad7ff..33c6ae11 100644 --- a/testsuite/E20.py +++ b/testsuite/E20.py @@ -48,6 +48,10 @@ x, y = y , x #: E203:1:37 foo[idxs[2 : 6] : spam(ham[1], {eggs : a[2 : 4]})] +#: E203:1:8 +[lambda : foo] +#: E203:1:13 +ham[lambda x : foo] #: Okay if x == 4: print x, y @@ -58,4 +62,6 @@ ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)] ham[lower + offset : upper + offset] foo[idxs[2 : 6] : spam(ham[1], {eggs: a[2 : 4]})] +[lambda: foo] +ham[lambda x: foo] #: From c88d21ecce0dd0a9d77b7bd2c88024809f8b6f13 Mon Sep 17 00:00:00 2001 From: DesbyP Date: Fri, 28 Feb 2020 19:42:40 +0100 Subject: [PATCH 3/3] ironically remove space to comply with flake8 --- pycodestyle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycodestyle.py b/pycodestyle.py index b4452d9e..b331889c 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -461,7 +461,7 @@ def is_a_slice(line, space_pos): # check for a lambda expression nested in brackets if space_pos > 6: last_opened = parentheses_brackets_braces[-1] - between_bracket_colon = line[last_opened[1] + 1 : space_pos] + between_bracket_colon = line[last_opened[1] + 1: space_pos] is_lambda_expr = 'lambda' in between_bracket_colon return (is_within_brackets and not is_lambda_expr)