PyCQA / pycodestyle

Simple Python style checker in one Python file

Home Page:https://pycodestyle.pycqa.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

E203: False positive "whitespace before ':' " on list slice.

aleksey-kutepov opened this issue · comments

I've encountered the problem in the following code:

a = [1, 2, 3, 4, 5]
b = a[1+1 : 2+2]  # E203
c = a[1 + 1 : 2 + 2]  # E203
d = a[1+1:2+2]

However, PEP8 chapter https://www.python.org/dev/peps/pep-0008/#whitespace-in-expressions-and-statements handles this as good style:

However, in a slice the colon acts like a binary operator, and should have equal amounts on either side (treating it as the operator with the lowest priority). In an extended slice, both colons must have the same amount of spacing applied. Exception: when a slice parameter is omitted, the space is omitted.

Yes:

ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
No:

ham[lower + offset:upper + offset]
ham[1: 9], ham[1 :9], ham[1:9 :3]
ham[lower : : upper]
ham[ : upper]

I took a look at the code and it looks like the function extraneous_whitespace
on line 271 in pep8.py is the function that needs to be updated. It is
returning "E03 whitespace before :" regardless of whether it is a legitimate.

This is confirmed in the doc string for the function, showing that E203 it covers these cases:
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

Looking at the testsuite E20.py confirmed that it is not accounting for legitimate uses of this case.

This needs to be cleaned up A LOT but here is a prototype of a potential fix(very incomplete but it is a workable idea)
it recognizes ham[lower + offset : upper + offset] as correct and [1 : 2] as incorrect.

Please keep in mind this was a quick very incomplete prototype.
If no one else has a better idea, then I would like to cleanup/finish this idea.

  1. a regex to recognize the colon used as an operator (this needs to be cleaned up and needs operators other than '+' added)
    COLON_AS_OPERATOR = re.compile(r'\S+\s_+\s_\S+\s:\s\S+\s_+\s_\S+')

  2. within the extraneous_whitespace function, it calls a new function to check if the colon is being used as an operator
    if char == ':':
    colon_is_operator = is_colon_operator(logical_line)

  3. The new function is_colon_operator checks to see if it matches the regex as defined
    in COLON_AS_OPERATOR
    def is_colon_operator(logical_line):
    found = 0
    for match in COLON_AS_OPERATOR.finditer(logical_line):
    found = 1
    return found

  4. if the colon is being used as an operator, it does not return an error. Otherwise (as in [1 : 2])
    it returns the 203 error
    if colon_is_operator == False:
    yield found, "%s whitespace before '%s'" % (code, char)

So I've been hacking on this for some time, and I don't know how to approach this without tokenizing. The wall I'm hitting is that the rules are context sensitive.

I've managed to hack the linter enough that it handles expressions inside [] specially. However that breaks when you have lists of dictionaries, where the rules "revert" back inside {}.

I have some code that somewhat works, but I'd be interested in some more guidance on how to tackle this.

Hi @vodik, you can add a parameter tokens, and if you need state (im not sure it is needed) see use of checker_state in module_imports_on_top_of_file.

this is now triggered by black doing the "right" thing - psf/black#279

@RonnyPfannschmidt I'd be happy to merge a PR that fixes this case

@sigmavirus24 i#ll takea short look if i can figure a quickfix

no quickfix for this one :(

Facing the same issue:

-        attr_name = adapter[last_dot + 1:]
+        attr_name = adapter[last_dot + 1 :]

As a temporary workaround, black will format the following without spacing (passing pycodestyle check):

i = last_dot + 1
attr_name = adapter[i:]

i simply disabled the check, as black is making those bits correct in all cases

Is there a way to align the projects pycodestyle and black so that users no longer need to manually configure them to work on a common repo? This alignment is now more important given that black has been moved into an official python repo: https://github.com/python/black

@cclauss did you mean aside from fixing this issue (#373 in pycodestyle)?

Slightly tangental, but I wrote flake8-black to let me run the black style checks as well as the pycodestyle checks from within flake8, see:

https://github.com/peterjc/flake8-black

I had to document ignoring E203 as a workaround.

Is there a way to align the projects pycodestyle and black so that users no longer need to manually configure them to work on a common repo? This alignment is now more important given that black has been moved into an official python repo: https://github.com/python/black

fwiw the move into the python project is because it is a PSF project now

From this:

Q: Does it mean it's official now?
A: No, just like mypy isn't.

Thought I would bump this issue to see where it stood on the priority list.

I encourage adopt black style since it's following PEP8. For now seems like the only solution is to silent this check in flake8 in my pre-commit hook. Hopefully this will be fixed soon. :D

I opened the following Pull Request (914) to disable this false alarm.
Do not hesitate to comment to push this forward.

Will this than also be disabled for the walrus operator?

eg.
if a := 5 + 3 > 6: print(f"yes {a}")

@tamenol this issue is specifically about list slice (as the title and examples indicate)

additionally, if you use the latest version of pycodestyle the walrus operator is understood and does not trigger an error:

$ ~/opt/venv/bin/pycodestyle -
if x := 1:
    print(x)
$ 

@asottile Ok, my bad

This also triggers a false positive:

def test():
    return "this is a very very long string to get to eighty characters"[
        (5 % 2) :: 4  # black adds spaces around the ::
    ]
commented

I still have this issue with flake8 v.3.8.3.

When definining a string slice like this:

my_string[my_string.rindex("/") + 1 : my_string.index(":")]

flake8 will raise violation E203.

However, this should not apply to slices, as mentioned in https://www.python.org/dev/peps/pep-0008/#pet-peeves.

Manually removing the spaces does not work in our case, as code formatting is done by black, which will automatically add whitepspaces around : when used in a slice.

Any news?

@jeffteixeira do you see a comment or resolution here? bumping the thread does not help