Skip to content

Commit 2c29cc4

Browse files
authored
Merge branch 'master' into master
2 parents 2f0ca0b + d219c68 commit 2c29cc4

File tree

6 files changed

+58
-11
lines changed

6 files changed

+58
-11
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ matrix:
1717
env: TOXENV=py36
1818
- python: 3.7
1919
env: TOXENV=py37
20+
- python: 3.8
21+
env: TOXENV=py38
2022
- python: pypy2.7-6.0
2123
env: TOXENV=pypy
2224
- python: pypy3.5-6.0

pycodestyle.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,13 @@ def lru_cache(maxsize=128): # noqa as it's a fake implementation.
117117
WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%'])
118118
# Warn for -> function annotation operator in py3.5+ (issue 803)
119119
FUNCTION_RETURN_ANNOTATION_OP = ['->'] if sys.version_info >= (3, 5) else []
120+
ASSIGNMENT_EXPRESSION_OP = [':='] if sys.version_info >= (3, 8) else []
120121
WS_NEEDED_OPERATORS = frozenset([
121122
'**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>',
122123
'%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=',
123124
'and', 'in', 'is', 'or'] +
124-
FUNCTION_RETURN_ANNOTATION_OP)
125+
FUNCTION_RETURN_ANNOTATION_OP +
126+
ASSIGNMENT_EXPRESSION_OP)
125127
WHITESPACE = frozenset(' \t')
126128
NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
127129
SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT])
@@ -134,7 +136,7 @@ def lru_cache(maxsize=128): # noqa as it's a fake implementation.
134136
RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$')
135137
ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b')
136138
DOCSTRING_REGEX = re.compile(r'u?r?["\']')
137-
EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({] | [\]}),;:]')
139+
EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({] | [\]}),;]| :(?!=)')
138140
WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
139141
COMPARE_SINGLETON_REGEX = re.compile(r'(\bNone|\bFalse|\bTrue)?\s*([=!]=)'
140142
r'\s*(?(1)|(None|False|True))\b')
@@ -495,13 +497,16 @@ def missing_whitespace(logical_line):
495497
line = logical_line
496498
for index in range(len(line) - 1):
497499
char = line[index]
498-
if char in ',;:' and line[index + 1] not in WHITESPACE:
500+
next_char = line[index + 1]
501+
if char in ',;:' and next_char not in WHITESPACE:
499502
before = line[:index]
500503
if char == ':' and before.count('[') > before.count(']') and \
501504
before.rfind('{') < before.rfind('['):
502505
continue # Slice syntax, no space required
503-
if char == ',' and line[index + 1] == ')':
506+
if char == ',' and next_char == ')':
504507
continue # Allow tuple with only one element: (3,)
508+
if char == ':' and next_char == '=' and sys.version_info >= (3, 8):
509+
continue # Allow assignment expression
505510
yield index, "E231 missing whitespace after '%s'" % char
506511

507512

@@ -1077,7 +1082,8 @@ def is_string_literal(line):
10771082
line = line[1:]
10781083
return line and (line[0] == '"' or line[0] == "'")
10791084

1080-
allowed_try_keywords = ('try', 'except', 'else', 'finally')
1085+
allowed_keywords = (
1086+
'try', 'except', 'else', 'finally', 'with', 'if', 'elif')
10811087

10821088
if indent_level: # Allow imports in conditional statement/function
10831089
return
@@ -1091,9 +1097,9 @@ def is_string_literal(line):
10911097
yield 0, "E402 module level import not at top of file"
10921098
elif re.match(DUNDER_REGEX, line):
10931099
return
1094-
elif any(line.startswith(kw) for kw in allowed_try_keywords):
1095-
# Allow try, except, else, finally keywords intermixed with
1096-
# imports in order to support conditional importing
1100+
elif any(line.startswith(kw) for kw in allowed_keywords):
1101+
# Allow certain keywords intermixed with imports in order to
1102+
# support conditional or filtered importing
10971103
return
10981104
elif is_string_literal(line):
10991105
# The first literal is a docstring, allow it. Otherwise, report
@@ -1145,7 +1151,9 @@ def compound_statements(logical_line):
11451151
update_counts(line[prev_found:found], counts)
11461152
if ((counts['{'] <= counts['}'] and # {'a': 1} (dict)
11471153
counts['['] <= counts[']'] and # [1:2] (slice)
1148-
counts['('] <= counts[')'])): # (annotation)
1154+
counts['('] <= counts[')']) and # (annotation)
1155+
not (sys.version_info >= (3, 8) and
1156+
line[found + 1] == '=')): # assignment expression
11491157
lambda_kw = LAMBDA_REGEX.search(line, 0, found)
11501158
if lambda_kw:
11511159
before = line[:lambda_kw.start()].rstrip()
@@ -1209,13 +1217,16 @@ def explicit_line_join(logical_line, tokens):
12091217
parens -= 1
12101218

12111219

1220+
_SYMBOLIC_OPS = frozenset("()[]{},:.;@=%~") | frozenset(("...",))
1221+
1222+
12121223
def _is_binary_operator(token_type, text):
12131224
is_op_token = token_type == tokenize.OP
12141225
is_conjunction = text in ['and', 'or']
12151226
# NOTE(sigmavirus24): Previously the not_a_symbol check was executed
12161227
# conditionally. Since it is now *always* executed, text may be
12171228
# None. In that case we get a TypeError for `text not in str`.
1218-
not_a_symbol = text and text not in "()[]{},:.;@=%~"
1229+
not_a_symbol = text and text not in _SYMBOLIC_OPS
12191230
# The % character is strictly speaking a binary operator, but the
12201231
# common usage seems to be to put it next to the format parameters,
12211232
# after a line break.

testsuite/E40.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@
3333
finally:
3434
print('made attempt to import foo')
3535

36+
import bar
37+
#: Okay
38+
with warnings.catch_warnings():
39+
warnings.filterwarnings("ignore", DeprecationWarning)
40+
import foo
41+
42+
import bar
43+
#: Okay
44+
if False:
45+
import foo
46+
elif not True:
47+
import bar
48+
else:
49+
import mwahaha
50+
3651
import bar
3752
#: E402
3853
VERSION = '1.2.3'

testsuite/python3.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,10 @@ class Class:
3737

3838
def m(self):
3939
xs: List[int] = []
40+
41+
42+
# Used to trigger W504
43+
def f(
44+
x: str = ...
45+
):
46+
...

testsuite/python38.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
#: Okay
22
def f(a, /, b):
33
pass
4+
#: Okay
5+
if x := 1:
6+
print(x)
7+
if m and (token := m.group(1)):
8+
pass
9+
stuff = [[y := f(x), x / y] for x in range(5)]
10+
#: E225:1:5
11+
if x:= 1:
12+
pass
13+
#: E225:1:18
14+
if False or (x :=1):
15+
pass

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# and then run "tox" from this directory.
55

66
[tox]
7-
envlist = py27, py34, py35, py36, py37, pypy, pypy3, jython
7+
envlist = py27, py34, py35, py36, py37, py38, pypy, pypy3, jython
88
skipsdist = True
99
skip_missing_interpreters = True
1010

0 commit comments

Comments
 (0)