11import math
22import tokenize
3- from typing import Iterator , List
3+ from typing import Iterable , List , Optional , Tuple
44
55from typing_extensions import final
66
@@ -36,23 +36,19 @@ def _is_target_line(self, token: tokenize.TokenInfo) -> bool:
3636
3737@final
3838class _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
8899class _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