diff options
author | Nikita Serba <nikitaserba@icloud.com> | 2020-01-22 20:34:06 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-22 20:34:06 +0200 |
commit | 2c29cc4f4f08906b111e306e6e806b5af628fbbd (patch) | |
tree | 5d2d5d421edd7872f5d8f4d4b63542e8cce9186f | |
parent | 2f0ca0b42660d7a6ee239f3a372a8783ad20c37a (diff) | |
parent | d219c684f117be77927d33146e76a5364161e518 (diff) | |
download | pep8-2c29cc4f4f08906b111e306e6e806b5af628fbbd.tar.gz |
Merge branch 'master' into master
-rw-r--r-- | .travis.yml | 2 | ||||
-rwxr-xr-x | pycodestyle.py | 31 | ||||
-rw-r--r-- | testsuite/E40.py | 15 | ||||
-rw-r--r-- | testsuite/python3.py | 7 | ||||
-rw-r--r-- | testsuite/python38.py | 12 | ||||
-rw-r--r-- | tox.ini | 2 |
6 files changed, 58 insertions, 11 deletions
diff --git a/.travis.yml b/.travis.yml index ce324be..696c1c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,8 @@ matrix: env: TOXENV=py36 - python: 3.7 env: TOXENV=py37 + - python: 3.8 + env: TOXENV=py38 - python: pypy2.7-6.0 env: TOXENV=pypy - python: pypy3.5-6.0 diff --git a/pycodestyle.py b/pycodestyle.py index 7b5b4f8..b7e0b2f 100755 --- a/pycodestyle.py +++ b/pycodestyle.py @@ -117,11 +117,13 @@ ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-']) WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%']) # Warn for -> function annotation operator in py3.5+ (issue 803) FUNCTION_RETURN_ANNOTATION_OP = ['->'] if sys.version_info >= (3, 5) else [] +ASSIGNMENT_EXPRESSION_OP = [':='] if sys.version_info >= (3, 8) else [] WS_NEEDED_OPERATORS = frozenset([ '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>', '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=', 'and', 'in', 'is', 'or'] + - FUNCTION_RETURN_ANNOTATION_OP) + FUNCTION_RETURN_ANNOTATION_OP + + ASSIGNMENT_EXPRESSION_OP) WHITESPACE = frozenset(' \t') NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE]) SKIP_TOKENS = NEWLINE.union([tokenize.INDENT, tokenize.DEDENT]) @@ -134,7 +136,7 @@ RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,') RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,.*,\s*\w+\s*$') ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b') DOCSTRING_REGEX = re.compile(r'u?r?["\']') -EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({] | [\]}),;:]') +EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[\[({] | [\]}),;]| :(?!=)') WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') COMPARE_SINGLETON_REGEX = re.compile(r'(\bNone|\bFalse|\bTrue)?\s*([=!]=)' r'\s*(?(1)|(None|False|True))\b') @@ -495,13 +497,16 @@ def missing_whitespace(logical_line): line = logical_line for index in range(len(line) - 1): char = line[index] - if char in ',;:' and line[index + 1] not in WHITESPACE: + next_char = line[index + 1] + if char in ',;:' and next_char not in WHITESPACE: before = line[:index] if char == ':' and before.count('[') > before.count(']') and \ before.rfind('{') < before.rfind('['): continue # Slice syntax, no space required - if char == ',' and line[index + 1] == ')': + if char == ',' and next_char == ')': continue # Allow tuple with only one element: (3,) + if char == ':' and next_char == '=' and sys.version_info >= (3, 8): + continue # Allow assignment expression yield index, "E231 missing whitespace after '%s'" % char @@ -1077,7 +1082,8 @@ def module_imports_on_top_of_file( line = line[1:] return line and (line[0] == '"' or line[0] == "'") - allowed_try_keywords = ('try', 'except', 'else', 'finally') + allowed_keywords = ( + 'try', 'except', 'else', 'finally', 'with', 'if', 'elif') if indent_level: # Allow imports in conditional statement/function return @@ -1091,9 +1097,9 @@ def module_imports_on_top_of_file( yield 0, "E402 module level import not at top of file" elif re.match(DUNDER_REGEX, line): return - elif any(line.startswith(kw) for kw in allowed_try_keywords): - # Allow try, except, else, finally keywords intermixed with - # imports in order to support conditional importing + elif any(line.startswith(kw) for kw in allowed_keywords): + # Allow certain keywords intermixed with imports in order to + # support conditional or filtered importing return elif is_string_literal(line): # The first literal is a docstring, allow it. Otherwise, report @@ -1145,7 +1151,9 @@ def compound_statements(logical_line): update_counts(line[prev_found:found], counts) if ((counts['{'] <= counts['}'] and # {'a': 1} (dict) counts['['] <= counts[']'] and # [1:2] (slice) - counts['('] <= counts[')'])): # (annotation) + counts['('] <= counts[')']) and # (annotation) + not (sys.version_info >= (3, 8) and + line[found + 1] == '=')): # assignment expression lambda_kw = LAMBDA_REGEX.search(line, 0, found) if lambda_kw: before = line[:lambda_kw.start()].rstrip() @@ -1209,13 +1217,16 @@ def explicit_line_join(logical_line, tokens): parens -= 1 +_SYMBOLIC_OPS = frozenset("()[]{},:.;@=%~") | frozenset(("...",)) + + def _is_binary_operator(token_type, text): is_op_token = token_type == tokenize.OP is_conjunction = text in ['and', 'or'] # NOTE(sigmavirus24): Previously the not_a_symbol check was executed # conditionally. Since it is now *always* executed, text may be # None. In that case we get a TypeError for `text not in str`. - not_a_symbol = text and text not in "()[]{},:.;@=%~" + not_a_symbol = text and text not in _SYMBOLIC_OPS # The % character is strictly speaking a binary operator, but the # common usage seems to be to put it next to the format parameters, # after a line break. diff --git a/testsuite/E40.py b/testsuite/E40.py index f9a18fc..6c71fa2 100644 --- a/testsuite/E40.py +++ b/testsuite/E40.py @@ -34,6 +34,21 @@ finally: print('made attempt to import foo') import bar +#: Okay +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", DeprecationWarning) + import foo + +import bar +#: Okay +if False: + import foo +elif not True: + import bar +else: + import mwahaha + +import bar #: E402 VERSION = '1.2.3' diff --git a/testsuite/python3.py b/testsuite/python3.py index be7c58f..709695a 100644 --- a/testsuite/python3.py +++ b/testsuite/python3.py @@ -37,3 +37,10 @@ class Class: def m(self): xs: List[int] = [] + + +# Used to trigger W504 +def f( + x: str = ... +): + ... diff --git a/testsuite/python38.py b/testsuite/python38.py index adf217d..5f62a3f 100644 --- a/testsuite/python38.py +++ b/testsuite/python38.py @@ -1,3 +1,15 @@ #: Okay def f(a, /, b): pass +#: Okay +if x := 1: + print(x) +if m and (token := m.group(1)): + pass +stuff = [[y := f(x), x / y] for x in range(5)] +#: E225:1:5 +if x:= 1: + pass +#: E225:1:18 +if False or (x :=1): + pass @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py34, py35, py36, py37, pypy, pypy3, jython +envlist = py27, py34, py35, py36, py37, py38, pypy, pypy3, jython skipsdist = True skip_missing_interpreters = True |