@@ -499,48 +499,6 @@ def missing_whitespace_after_keyword(logical_line, tokens):
499
499
yield tok0 .end , "E275 missing whitespace after keyword"
500
500
501
501
502
- @register_check
503
- def missing_whitespace (logical_line , tokens ):
504
- r"""Each comma, semicolon or colon should be followed by whitespace.
505
-
506
- Okay: [a, b]
507
- Okay: (3,)
508
- Okay: a[3,] = 1
509
- Okay: a[1:4]
510
- Okay: a[:4]
511
- Okay: a[1:]
512
- Okay: a[1:4:2]
513
- E231: ['a','b']
514
- E231: foo(bar,baz)
515
- E231: [{'a':'b'}]
516
- """
517
- brace_stack = []
518
- for tok in tokens :
519
- if tok .type == tokenize .OP and tok .string in {'[' , '(' , '{' }:
520
- brace_stack .append (tok .string )
521
- elif tok .type == FSTRING_START :
522
- brace_stack .append ('f' )
523
- elif brace_stack :
524
- if tok .type == tokenize .OP and tok .string in {']' , ')' , '}' }:
525
- brace_stack .pop ()
526
- elif tok .type == FSTRING_END :
527
- brace_stack .pop ()
528
-
529
- if tok .type == tokenize .OP and tok .string in {',' , ';' , ':' }:
530
- next_char = tok .line [tok .end [1 ]:tok .end [1 ] + 1 ]
531
- if next_char not in WHITESPACE and next_char not in '\r \n ' :
532
- # slice
533
- if tok .string == ':' and brace_stack [- 1 :] == ['[' ]:
534
- continue
535
- # 3.12+ fstring format specifier
536
- elif tok .string == ':' and brace_stack [- 2 :] == ['f' , '{' ]:
537
- continue
538
- # tuple (and list for some reason?)
539
- elif tok .string == ',' and next_char in ')]' :
540
- continue
541
- yield tok .end , f'E231 missing whitespace after { tok .string !r} '
542
-
543
-
544
502
@register_check
545
503
def indentation (logical_line , previous_logical , indent_char ,
546
504
indent_level , previous_indent_level ,
@@ -856,14 +814,16 @@ def whitespace_around_operator(logical_line):
856
814
857
815
858
816
@register_check
859
- def missing_whitespace_around_operator (logical_line , tokens ):
860
- r"""Surround operators with a single space on either side .
817
+ def missing_whitespace (logical_line , tokens ):
818
+ r"""Surround operators with the correct amount of whitespace .
861
819
862
820
- Always surround these binary operators with a single space on
863
821
either side: assignment (=), augmented assignment (+=, -= etc.),
864
822
comparisons (==, <, >, !=, <=, >=, in, not in, is, is not),
865
823
Booleans (and, or, not).
866
824
825
+ - Each comma, semicolon or colon should be followed by whitespace.
826
+
867
827
- If operators with different priorities are used, consider adding
868
828
whitespace around the operators with the lowest priorities.
869
829
@@ -874,6 +834,13 @@ def missing_whitespace_around_operator(logical_line, tokens):
874
834
Okay: c = (a + b) * (a - b)
875
835
Okay: foo(bar, key='word', *args, **kwargs)
876
836
Okay: alpha[:-i]
837
+ Okay: [a, b]
838
+ Okay: (3,)
839
+ Okay: a[3,] = 1
840
+ Okay: a[1:4]
841
+ Okay: a[:4]
842
+ Okay: a[1:]
843
+ Okay: a[1:4:2]
877
844
878
845
E225: i=i+1
879
846
E225: submitted +=1
@@ -884,19 +851,52 @@ def missing_whitespace_around_operator(logical_line, tokens):
884
851
E226: hypot2 = x*x + y*y
885
852
E227: c = a|b
886
853
E228: msg = fmt%(errno, errmsg)
854
+ E231: ['a','b']
855
+ E231: foo(bar,baz)
856
+ E231: [{'a':'b'}]
887
857
"""
888
- parens = 0
889
858
need_space = False
890
859
prev_type = tokenize .OP
891
860
prev_text = prev_end = None
892
861
operator_types = (tokenize .OP , tokenize .NAME )
862
+ brace_stack = []
893
863
for token_type , text , start , end , line in tokens :
864
+ if token_type == tokenize .OP and text in {'[' , '(' , '{' }:
865
+ brace_stack .append (text )
866
+ elif token_type == FSTRING_START :
867
+ brace_stack .append ('f' )
868
+ elif token_type == tokenize .NAME and text == 'lambda' :
869
+ brace_stack .append ('l' )
870
+ elif brace_stack :
871
+ if token_type == tokenize .OP and text in {']' , ')' , '}' }:
872
+ brace_stack .pop ()
873
+ elif token_type == FSTRING_END :
874
+ brace_stack .pop ()
875
+ elif (
876
+ brace_stack [- 1 ] == 'l' and
877
+ token_type == tokenize .OP and
878
+ text == ':'
879
+ ):
880
+ brace_stack .pop ()
881
+
894
882
if token_type in SKIP_COMMENTS :
895
883
continue
896
- if text in ('(' , 'lambda' ):
897
- parens += 1
898
- elif text == ')' :
899
- parens -= 1
884
+
885
+ if token_type == tokenize .OP and text in {',' , ';' , ':' }:
886
+ next_char = line [end [1 ]:end [1 ] + 1 ]
887
+ if next_char not in WHITESPACE and next_char not in '\r \n ' :
888
+ # slice
889
+ if text == ':' and brace_stack [- 1 :] == ['[' ]:
890
+ pass
891
+ # 3.12+ fstring format specifier
892
+ elif text == ':' and brace_stack [- 2 :] == ['f' , '{' ]:
893
+ pass
894
+ # tuple (and list for some reason?)
895
+ elif text == ',' and next_char in ')]' :
896
+ pass
897
+ else :
898
+ yield end , f'E231 missing whitespace after { text !r} '
899
+
900
900
if need_space :
901
901
if start != prev_end :
902
902
# Found a (probably) needed space
@@ -933,7 +933,7 @@ def missing_whitespace_around_operator(logical_line, tokens):
933
933
"around %s operator" % (code , optype ))
934
934
need_space = False
935
935
elif token_type in operator_types and prev_end is not None :
936
- if text == '=' and parens :
936
+ if text == '=' and brace_stack and brace_stack [ - 1 ] in { 'l' , '(' } :
937
937
# Allow keyword args or defaults: foo(bar=None).
938
938
pass
939
939
elif text in WS_NEEDED_OPERATORS :
0 commit comments