blob: e8b97d7a3adfed62e3048c479fc75832343e051b [file]
# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Yet Another Python Formatter.
YAPF uses the algorithm in clang-format to figure out the "best" formatting for
Python code. It looks at the program as a series of "unwrappable lines" ---
i.e., lines which, if there were no column limit, we would place all tokens on
that line. It then uses a priority queue to figure out what the best formatting
is --- i.e., the formatting with the least penalty.
It differs from tools like autopep8 and pep8ify in that it doesn't just look for
violations of the style guide, but looks at the module as a whole, making
formatting decisions based on what's the best format for each line.
If no filenames are specified, YAPF reads the code from stdin.
"""
import argparse
import logging
import sys
from yapf.yapflib import file_resources
from yapf.yapflib import yapf_api
__version__ = '0.1'
def main(argv):
"""Main program.
Arguments:
argv: (Positional arguments) A list of files to reformat.
Returns:
0 if there were no errors, non-zero otherwise.
"""
parser = argparse.ArgumentParser(description='Formatter for Python code.')
diff_inplace_group = parser.add_mutually_exclusive_group()
diff_inplace_group.add_argument(
'-d', '--diff', action='store_true',
help='print the diff for the fixed source')
diff_inplace_group.add_argument(
'-i', '--in-place', action='store_true',
help='make changes to files in place')
lines_recursive_group = parser.add_mutually_exclusive_group()
lines_recursive_group.add_argument(
'-l', '--lines', metavar='START-END', action='append', default=None,
help='range of lines to reformat, one-based')
lines_recursive_group.add_argument(
'-r', '--recursive', action='store_true',
help='run recursively over directories')
parser.add_argument('files', nargs=argparse.REMAINDER)
args = parser.parse_args()
if args.lines and len(args.files) > 1:
parser.error('cannot use -l/--lines with more than one file')
lines = _GetLines(args.lines) if args.lines is not None else None
files = file_resources.GetCommandLineFiles(argv[1:], args.recursive)
if not files:
# No arguments specified. Read code from stdin.
if args.in_place or args.diff:
parser.error('cannot use --in_place or --diff flags when reading '
'from stdin')
original_source = []
while True:
try:
# Use 'raw_input' instead of 'sys.stdin.read', because otherwise the
# user will need to hit 'Ctrl-D' more than once if they're inputting
# the program by hand. 'raw_input' throws an EOFError exception if
# 'Ctrl-D' is pressed, which makes it easy to bail out of this loop.
original_source.append(raw_input())
except EOFError:
break
sys.stdout.write(yapf_api.FormatCode(
unicode('\n'.join(original_source) + '\n'),
filename='<stdin>',
lines=lines))
return 0
FormatFiles(files, lines, args.in_place)
return 0
def FormatFiles(filenames, lines, in_place=False):
"""Format a list of files.
Arguments:
filenames: (list of unicode) A list of files to reformat.
lines: (list of tuples of integers) A list of tuples of lines, [start, end],
that we want to format. The lines are 1-based indexed. This argument
overrides the 'args.lines'. It can be used by third-party code (e.g.,
IDEs) when reformatting a snippet of code.
in_place: (bool) Modify the files in place.
"""
for filename in filenames:
logging.info('Reformatting %s', filename)
reformatted_code = yapf_api.FormatFile(filename, lines)
if reformatted_code is not None:
file_resources.WriteReformattedCode(filename, reformatted_code, in_place)
def _GetLines(line_strings):
"""Parses the start and end lines from a line string like 'start-end'.
Arguments:
line_strings: (array of string) A list of strings representing a line
range like 'start-end'.
Returns:
A list of tuples of the start and end line numbers.
Raises:
ValueError: If the line string failed to parse or was an invalid line range.
"""
lines = []
for line_string in line_strings:
line = map(int, line_string.split('-', 1))
if line[0] < 1:
raise ValueError('invalid start of line range: %r' % line)
if line[0] > line[1]:
raise ValueError('end comes before start in line range: %r', line)
lines.append(tuple(line))
return lines
if __name__ == '__main__':
sys.exit(main(sys.argv))