Skip to content

Commit 18276e6

Browse files
authored
Version 0.19.2 (#2900)
1 parent 99ba44e commit 18276e6

5 files changed

Lines changed: 62 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ Semantic versioning in our case means:
1717
change the client facing API, change code conventions significantly, etc.
1818

1919

20+
## 0.19.2
21+
22+
### Bugfixes
23+
24+
- Fixes `WrongEmptyLinesCountViolation` crash on `Callable[..., ...]` #2899
25+
26+
2027
## 0.19.1
2128

2229
This release fixes how `...` is used. For example, it is common to define

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "wemake-python-styleguide"
3-
version = "0.19.1"
3+
version = "0.19.2"
44
description = "The strictest and most opinionated python linter ever"
55

66
license = "MIT"

tests/test_visitors/test_ast/test_classes/test_wrong_empty_lines_count.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from wemake_python_styleguide.violations.best_practices import (
55
WrongEmptyLinesCountViolation,
66
)
7-
from wemake_python_styleguide.visitors.ast.function_empty_lines import (
7+
from wemake_python_styleguide.visitors.tokenize.functions import (
88
WrongEmptyLinesCountVisitor,
99
)
1010

@@ -228,6 +228,16 @@ def kk() -> None:
228228
)
229229
"""
230230

231+
# https://github.com/wemake-services/wemake-python-styleguide/issues/2899
232+
regression2899_1 = """
233+
def curry(function: Callable[..., _ReturnType]) -> Callable[..., _ReturnType]:
234+
...
235+
"""
236+
237+
regression2899_2 = """
238+
def cur(function: Callable[..., _ReturnType]) -> Callable[..., _ReturnType]: ...
239+
"""
240+
231241

232242
@pytest.mark.parametrize('input_', [
233243
class_with_wrong_method,
@@ -262,6 +272,10 @@ def test_wrong(
262272
class_with_attributes,
263273
expression_without_function,
264274
module_level_empty_lines,
275+
276+
# Do not report anything for `Callable[...]`
277+
regression2899_1,
278+
regression2899_2,
265279
])
266280
def test_success(
267281
template,
@@ -304,7 +318,6 @@ def test_zero_option(
304318
def test_zero_option_with_valid_method(
305319
template,
306320
parse_tokens,
307-
default_options,
308321
assert_errors,
309322
options,
310323
mode,
@@ -357,7 +370,6 @@ def test_string_concatination(
357370
parse_tokens,
358371
default_options,
359372
assert_errors,
360-
options,
361373
mode,
362374
):
363375
"""Test function with multiline implicit string concatenation."""

wemake_python_styleguide/presets/types/tree.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
conditions,
1212
decorators,
1313
exceptions,
14-
function_empty_lines,
1514
functions,
1615
imports,
1716
iterables,
@@ -23,6 +22,9 @@
2322
statements,
2423
subscripts,
2524
)
25+
from wemake_python_styleguide.visitors.tokenize import (
26+
functions as tokenize_functions,
27+
)
2628

2729
#: Used to store all general visitors to be later passed to checker:
2830
PRESET: Final = (
@@ -57,6 +59,7 @@
5759
functions.UnnecessaryLiteralsVisitor,
5860
functions.FunctionSignatureVisitor,
5961
functions.FloatingNanCallVisitor,
62+
tokenize_functions.WrongEmptyLinesCountVisitor,
6063

6164
exceptions.WrongTryExceptVisitor,
6265
exceptions.NestedTryBlocksVisitor,
@@ -110,8 +113,6 @@
110113

111114
redundancy.RedundantEnumerateVisitor,
112115

113-
function_empty_lines.WrongEmptyLinesCountVisitor,
114-
115116
# Modules:
116117
modules.EmptyModuleContentsVisitor,
117118
modules.MagicModuleFunctionsVisitor,

wemake_python_styleguide/visitors/ast/function_empty_lines.py renamed to wemake_python_styleguide/visitors/tokenize/functions.py

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import math
22
import tokenize
3-
from typing import Iterator, List
3+
from typing import Iterable, List, Optional, Tuple
44

55
from typing_extensions import final
66

@@ -36,23 +36,19 @@ def _is_target_line(self, token: tokenize.TokenInfo) -> bool:
3636

3737
@final
3838
class _FileFunctions:
39-
4039
def __init__(self, file_tokens: List[tokenize.TokenInfo]) -> None:
4140
self._file_tokens = file_tokens
4241

43-
def as_list(self) -> List[_Function]:
44-
return list(self._search_functions())
45-
46-
def _search_functions(self) -> Iterator[_Function]:
42+
def search_functions(self) -> Iterable[_Function]: # noqa: WPS210
4743
function_tokens: List[tokenize.TokenInfo] = []
4844
in_function = False
4945
function_start_token = (0, 0)
50-
for token in self._file_tokens:
46+
for token_index, token in enumerate(self._file_tokens):
5147
function_ended = self._is_function_end(
5248
token,
53-
bool(function_tokens),
54-
function_start_token[1],
55-
function_start_token[0],
49+
token_index,
50+
function_start_token,
51+
function_tokens_exists=bool(function_tokens),
5652
)
5753
if not in_function and self._is_function_start(token):
5854
in_function = True
@@ -71,18 +67,33 @@ def _is_function_start(self, token: tokenize.TokenInfo) -> bool:
7167
def _is_function_end(
7268
self,
7369
token: tokenize.TokenInfo,
70+
token_index: int,
71+
function_start: Tuple[int, int],
72+
*,
7473
function_tokens_exists: bool,
75-
function_start_column: int,
76-
function_start_line: int,
7774
) -> bool:
78-
is_elipsis = token.string == '...'
79-
is_elipsis_end = is_elipsis and token.start[0] == function_start_line
75+
next_token = self._next_token(token_index)
76+
is_elipsis_end = (
77+
next_token and
78+
next_token.exact_type == tokenize.NEWLINE and
79+
token.string == '...' and
80+
token.start[0] == function_start[0]
81+
)
8082
if is_elipsis_end:
8183
return True
82-
column_valid = token.start[1] in {0, function_start_column}
84+
column_valid = token.start[1] in {0, function_start[1]}
8385
is_dedent_token = token.type == tokenize.DEDENT
8486
return is_dedent_token and function_tokens_exists and column_valid
8587

88+
def _next_token(
89+
self,
90+
token_index: int,
91+
) -> Optional[tokenize.TokenInfo]:
92+
try:
93+
return self._file_tokens[token_index + 1]
94+
except IndexError:
95+
return None
96+
8697

8798
@final
8899
class _FileTokens:
@@ -95,12 +106,13 @@ def __init__(
95106
self._file_functions = file_functions
96107
self._exps_for_one_empty_line = exps_for_one_empty_line
97108

98-
def analyze(self) -> List[best_practices.WrongEmptyLinesCountViolation]:
99-
violations = []
100-
for function in self._file_functions.as_list():
109+
def analyze(self) -> Iterable[best_practices.WrongEmptyLinesCountViolation]:
110+
for function in self._file_functions.search_functions():
101111
splitted_function_body = function.body().strip().split('\n')
102112
empty_lines_count = len([
103-
line for line in splitted_function_body if line == ''
113+
line
114+
for line in splitted_function_body
115+
if line == ''
104116
])
105117
if not empty_lines_count:
106118
continue
@@ -109,14 +121,11 @@ def analyze(self) -> List[best_practices.WrongEmptyLinesCountViolation]:
109121
len(splitted_function_body), empty_lines_count,
110122
)
111123
if empty_lines_count > available_empty_lines:
112-
violations.append(
113-
best_practices.WrongEmptyLinesCountViolation(
114-
function.name_token(),
115-
text=str(empty_lines_count),
116-
baseline=available_empty_lines,
117-
),
124+
yield best_practices.WrongEmptyLinesCountViolation(
125+
function.name_token(),
126+
text=str(empty_lines_count),
127+
baseline=available_empty_lines,
118128
)
119-
return violations
120129

121130
def _available_empty_lines(
122131
self,

0 commit comments

Comments
 (0)