@@ -62,16 +62,25 @@ def __init__(self):
6262 self .last_comment_lineno = 0
6363 self .last_was_decorator = False
6464 self .last_was_class_or_function = False
65+ self ._prev_stmt = None
6566
6667 def Visit_simple_stmt (self , node ): # pylint: disable=invalid-name
6768 self .DefaultNodeVisit (node )
6869 if node .children [0 ].type == grammar_token .COMMENT :
6970 self .last_comment_lineno = node .children [0 ].lineno
71+ else :
72+ # Do NOT set _prev_stmt on pure comment lines; keep the last real stmt.
73+ self ._prev_stmt = node
7074
7175 def Visit_decorator (self , node ): # pylint: disable=invalid-name
76+ func = _DecoratedFuncdef (node )
7277 if (self .last_comment_lineno and
7378 self .last_comment_lineno == node .children [0 ].lineno - 1 ):
7479 _SetNumNewlines (node .children [0 ], _NO_BLANK_LINES )
80+ elif self .last_was_decorator :
81+ _SetNumNewlines (node .children [0 ], _NO_BLANK_LINES )
82+ elif func is not None and self ._prev_stmt is not None and _MethodsInSameClass (self ._prev_stmt , func ):
83+ _SetNumNewlines (node .children [0 ], max (_ONE_BLANK_LINE , 1 + style .Get ('BLANK_LINES_BETWEEN_CLASS_DEFS' )))
7584 else :
7685 _SetNumNewlines (node .children [0 ], self ._GetNumNewlines (node ))
7786 for child in node .children :
@@ -87,6 +96,7 @@ def Visit_classdef(self, node): # pylint: disable=invalid-name
8796 self .Visit (child )
8897 self .class_level -= 1
8998 self .last_was_class_or_function = True
99+ self ._prev_stmt = node
90100
91101 def Visit_funcdef (self , node ): # pylint: disable=invalid-name
92102 self .last_was_class_or_function = False
@@ -103,6 +113,7 @@ def Visit_funcdef(self, node): # pylint: disable=invalid-name
103113 self .Visit (child )
104114 self .function_level -= 1
105115 self .last_was_class_or_function = True
116+ self ._prev_stmt = node
106117
107118 def DefaultNodeVisit (self , node ):
108119 """Override the default visitor for Node.
@@ -156,7 +167,11 @@ def _GetNumNewlines(self, node):
156167 return _NO_BLANK_LINES
157168 elif self ._IsTopLevel (node ):
158169 return 1 + style .Get ('BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION' )
159- return _ONE_BLANK_LINE
170+ elif self ._prev_stmt is not None and _MethodsInSameClass (self ._prev_stmt , node ):
171+ # Only between consecutive methods *in the same class*.
172+ # Keep at least one blank line as a floor (to avoid 0 if user misconfigures).
173+ return max (_ONE_BLANK_LINE , 1 + style .Get ('BLANK_LINES_BETWEEN_CLASS_DEFS' ))
174+ return _NO_BLANK_LINES
160175
161176 def _IsTopLevel (self , node ):
162177 return (not (self .class_level or self .function_level ) and
@@ -175,3 +190,24 @@ def _StartsInZerothColumn(node):
175190
176191def _AsyncFunction (node ):
177192 return (node .prev_sibling and node .prev_sibling .type == grammar_token .ASYNC )
193+
194+
195+ def _MethodsInSameClass (prev_node , curr_node ):
196+ # 1) Walk up from each node to find the nearest *enclosing function* (def …).
197+ prev_func = pytree_utils .EnclosingFunc (prev_node )
198+ curr_func = pytree_utils .EnclosingFunc (curr_node )
199+
200+ # 2) If either enclosing thing is not actually a function definition, bail out.
201+ if not (pytree_utils .IsFuncDef (prev_func ) and pytree_utils .IsFuncDef (curr_func )):
202+ return False
203+
204+ # 3) From each function, walk up to find the *enclosing class* (class …).
205+ prev_cls = pytree_utils .EnclosingClass (prev_func .parent )
206+ curr_cls = pytree_utils .EnclosingClass (curr_func .parent )
207+
208+ # 4) True only if both functions live inside a class, and it’s the *same class node*.
209+ return prev_cls is not None and prev_cls is curr_cls
210+
211+
212+ def _DecoratedFuncdef (node ):
213+ return pytree_utils .DecoratedTarget (node , ('funcdef' ,))
0 commit comments