blob: a95590209fbb20aad86d5fe03c1c4a134d205f60 [file] [log] [blame]
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A 2to3 fixer that reformats docstrings.
This should transform docstrings to be closer to the conventions in pep-0257;
see https://www.python.org/dev/peps/pep-0257/.
"""
import re
from lib2to3.fixer_base import BaseFix
from lib2to3.pgen2 import token
from lib2to3.pygram import python_symbols
class FixDocstrings(BaseFix):
explicit = True
_accept_type = token.STRING
def match(self, node):
"""Returns True if the given node appears to be a docstring.
Docstrings should always have no previous siblings, and should be
direct children of simple_stmt.
Note: This may also match for some edge cases where there are
simple_stmt strings that aren't the first thing in a module, class
or function, and thus aren't considered docstrings; but changing these
strings should not change behavior.
"""
# Pylint incorrectly warns that there's no member simple_stmt on python_symbols
# because the attribute is set dynamically. pylint: disable=no-member
return (node.value.startswith('"""') and
node.prev_sibling is None and
node.parent.type == python_symbols.simple_stmt)
def transform(self, node, results):
# First, strip whitespace at the beginning and end.
node.value = re.sub(r'^"""\s+', '"""', node.value)
node.value = re.sub(r'\s+"""$', '"""', node.value)
# For multi-line docstrings, the closing quotes should go on their own line.
if '\n' in node.value:
indent = self._find_indent(node)
node.value = re.sub(r'"""$', '\n' + indent + '"""', node.value)
node.changed()
def _find_indent(self, node):
"""Returns the indentation level of the docstring."""
# The parent is assumed to be a simple_stmt (the docstring statement)
# either preceded by an indentation, or nothing.
if not node.parent.prev_sibling or node.parent.prev_sibling.type != token.INDENT:
return ''
return node.parent.prev_sibling.value