Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,21 @@ optional arguments:
pass
```

#### `BLANK_LINES_BETWEEN_CLASS_DEFS`

> Sets the number of desired blank lines between methods inside
> class definitions. For example:

```python
class Foo:
def method1():
pass
# <------ multiple
# <------ blank lines here
def method2():
pass
```

#### `BLANK_LINE_BEFORE_CLASS_DOCSTRING`

> Insert a blank line before a class-level docstring.
Expand Down
38 changes: 37 additions & 1 deletion yapf/pytree/blank_line_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,25 @@ def __init__(self):
self.last_comment_lineno = 0
self.last_was_decorator = False
self.last_was_class_or_function = False
self._prev_stmt = None

def Visit_simple_stmt(self, node): # pylint: disable=invalid-name
self.DefaultNodeVisit(node)
if node.children[0].type == grammar_token.COMMENT:
self.last_comment_lineno = node.children[0].lineno
else:
# Do NOT set _prev_stmt on pure comment lines; keep the last real stmt.
self._prev_stmt = node

def Visit_decorator(self, node): # pylint: disable=invalid-name
func = _DecoratedFuncdef(node)
if (self.last_comment_lineno and
self.last_comment_lineno == node.children[0].lineno - 1):
_SetNumNewlines(node.children[0], _NO_BLANK_LINES)
elif self.last_was_decorator:
_SetNumNewlines(node.children[0], _NO_BLANK_LINES)
elif func is not None and self._prev_stmt is not None and _MethodsInSameClass(self._prev_stmt, func):
_SetNumNewlines(node.children[0], max(_ONE_BLANK_LINE, 1 + style.Get('BLANK_LINES_BETWEEN_CLASS_DEFS')))
else:
_SetNumNewlines(node.children[0], self._GetNumNewlines(node))
for child in node.children:
Expand All @@ -87,6 +96,7 @@ def Visit_classdef(self, node): # pylint: disable=invalid-name
self.Visit(child)
self.class_level -= 1
self.last_was_class_or_function = True
self._prev_stmt = node

def Visit_funcdef(self, node): # pylint: disable=invalid-name
self.last_was_class_or_function = False
Expand All @@ -103,6 +113,7 @@ def Visit_funcdef(self, node): # pylint: disable=invalid-name
self.Visit(child)
self.function_level -= 1
self.last_was_class_or_function = True
self._prev_stmt = node

def DefaultNodeVisit(self, node):
"""Override the default visitor for Node.
Expand Down Expand Up @@ -156,7 +167,11 @@ def _GetNumNewlines(self, node):
return _NO_BLANK_LINES
elif self._IsTopLevel(node):
return 1 + style.Get('BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION')
return _ONE_BLANK_LINE
elif self._prev_stmt is not None and _MethodsInSameClass(self._prev_stmt, node):
# Only between consecutive methods *in the same class*.
# Keep at least one blank line as a floor (to avoid 0 if user misconfigures).
return max(_ONE_BLANK_LINE, 1 + style.Get('BLANK_LINES_BETWEEN_CLASS_DEFS'))
return _NO_BLANK_LINES

def _IsTopLevel(self, node):
return (not (self.class_level or self.function_level) and
Expand All @@ -175,3 +190,24 @@ def _StartsInZerothColumn(node):

def _AsyncFunction(node):
return (node.prev_sibling and node.prev_sibling.type == grammar_token.ASYNC)


def _MethodsInSameClass(prev_node, curr_node):
# 1) Walk up from each node to find the nearest *enclosing function* (def …).
prev_func = pytree_utils.EnclosingFunc(prev_node)
curr_func = pytree_utils.EnclosingFunc(curr_node)

# 2) If either enclosing thing is not actually a function definition, bail out.
if not (pytree_utils.IsFuncDef(prev_func) and pytree_utils.IsFuncDef(curr_func)):
return False

# 3) From each function, walk up to find the *enclosing class* (class …).
prev_cls = pytree_utils.EnclosingClass(prev_func.parent)
curr_cls = pytree_utils.EnclosingClass(curr_func.parent)

# 4) True only if both functions live inside a class, and it’s the *same class node*.
return prev_cls is not None and prev_cls is curr_cls


def _DecoratedFuncdef(node):
return pytree_utils.DecoratedTarget(node, ('funcdef',))
31 changes: 31 additions & 0 deletions yapf/pytree/pytree_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,34 @@ def _PytreeNodeRepr(node):
def IsCommentStatement(node):
return (NodeName(node) == 'simple_stmt' and
node.children[0].type == token.COMMENT)


def AscendTo(node, target_names):
n = node
while n is not None and NodeName(n) not in target_names:
n = getattr(n, 'parent', None)
return n if n is not None and NodeName(n) in target_names else None


def EnclosingFunc(node):
return node if NodeName(node) == 'funcdef' else AscendTo(node, {'funcdef'})


def EnclosingClass(node):
return node if NodeName(node) == 'classdef' else AscendTo(node, {'classdef'})


def IsFuncDef(node):
return node is not None and NodeName(node) == 'funcdef'


def IsClassDef(node):
return node is not None and NodeName(node) == 'classdef'


def DecoratedTarget(node, target_names=('funcdef', 'classdef')):
n = node
while n.next_sibling is not None and NodeName(n.next_sibling) == 'decorator':
n = n.next_sibling
cand = n.next_sibling
return cand if cand is not None and NodeName(cand) in target_names else None
14 changes: 14 additions & 0 deletions yapf/yapflib/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ class Foo:
def method():
pass
"""),
BLANK_LINES_BETWEEN_CLASS_DEFS=textwrap.dedent("""\
Sets the number of desired blank lines between methods inside
class definitions. For example:

class Foo:
def method1():
pass
# <------ multiple
# <------ blank lines
def method2():
pass
"""),
BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=textwrap.dedent("""\
Number of blank lines surrounding top-level function and class
definitions.
Expand Down Expand Up @@ -486,6 +498,7 @@ def CreatePEP8Style():
BLANK_LINE_BEFORE_MODULE_DOCSTRING=False,
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=True,
BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=2,
BLANK_LINES_BETWEEN_CLASS_DEFS=1,
BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES=1,
COALESCE_BRACKETS=False,
COLUMN_LIMIT=79,
Expand Down Expand Up @@ -675,6 +688,7 @@ def _IntOrIntListConverter(s):
BLANK_LINE_BEFORE_MODULE_DOCSTRING=_BoolConverter,
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF=_BoolConverter,
BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION=int,
BLANK_LINES_BETWEEN_CLASS_DEFS=int,
BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES=int,
COALESCE_BRACKETS=_BoolConverter,
COLUMN_LIMIT=int,
Expand Down