mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-04-03 13:41:09 +02:00
304 lines
10 KiB
Python
304 lines
10 KiB
Python
# Copyright 2015-2017 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.
|
|
"""YAPF.
|
|
|
|
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.
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
from yapf.yapflib import errors
|
|
from yapf.yapflib import file_resources
|
|
from yapf.yapflib import py3compat
|
|
from yapf.yapflib import style
|
|
from yapf.yapflib import yapf_api
|
|
|
|
__version__ = '0.16.2'
|
|
|
|
|
|
def main(argv):
|
|
"""Main program.
|
|
|
|
Arguments:
|
|
argv: command-line arguments, such as sys.argv (including the program name
|
|
in argv[0]).
|
|
|
|
Returns:
|
|
0 if there were no changes, non-zero otherwise.
|
|
|
|
Raises:
|
|
YapfError: if none of the supplied files were Python files.
|
|
"""
|
|
parser = argparse.ArgumentParser(description='Formatter for Python code.')
|
|
parser.add_argument(
|
|
'-v',
|
|
'--version',
|
|
action='store_true',
|
|
help='show version number and exit')
|
|
|
|
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(
|
|
'-r',
|
|
'--recursive',
|
|
action='store_true',
|
|
help='run recursively over directories')
|
|
lines_recursive_group.add_argument(
|
|
'-l',
|
|
'--lines',
|
|
metavar='START-END',
|
|
action='append',
|
|
default=None,
|
|
help='range of lines to reformat, one-based')
|
|
|
|
parser.add_argument(
|
|
'-e',
|
|
'--exclude',
|
|
metavar='PATTERN',
|
|
action='append',
|
|
default=None,
|
|
help='patterns for files to exclude from formatting')
|
|
parser.add_argument(
|
|
'--style',
|
|
action='store',
|
|
help=('specify formatting style: either a style name (for example "pep8" '
|
|
'or "google"), or the name of a file with style settings. The '
|
|
'default is pep8 unless a %s or %s file located in one of the '
|
|
'parent directories of the source file (or current directory for '
|
|
'stdin)' % (style.LOCAL_STYLE, style.SETUP_CONFIG)))
|
|
parser.add_argument(
|
|
'--style-help',
|
|
action='store_true',
|
|
help=('show style settings and exit; this output can be '
|
|
'saved to .style.yapf to make your settings '
|
|
'permanent'))
|
|
parser.add_argument(
|
|
'--no-local-style',
|
|
action='store_true',
|
|
help="don't search for local style definition")
|
|
parser.add_argument('--verify', action='store_true', help=argparse.SUPPRESS)
|
|
parser.add_argument(
|
|
'-p',
|
|
'--parallel',
|
|
action='store_true',
|
|
help=('Run yapf in parallel when formatting multiple files. Requires '
|
|
'concurrent.futures in Python 2.X'))
|
|
|
|
parser.add_argument('files', nargs='*')
|
|
args = parser.parse_args(argv[1:])
|
|
|
|
if args.version:
|
|
print('yapf {}'.format(__version__))
|
|
return 0
|
|
|
|
if args.style_help:
|
|
style.SetGlobalStyle(style.CreateStyleFromConfig(args.style))
|
|
print('[style]')
|
|
for option, docstring in sorted(style.Help().items()):
|
|
for line in docstring.splitlines():
|
|
print('#', line and ' ' or '', line, sep='')
|
|
print(option.lower(), '=', style.Get(option), sep='')
|
|
print()
|
|
return 0
|
|
|
|
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
|
|
if not args.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(py3compat.raw_input())
|
|
except EOFError:
|
|
break
|
|
|
|
style_config = args.style
|
|
if style_config is None and not args.no_local_style:
|
|
style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
|
|
|
|
source = [line.rstrip() for line in original_source]
|
|
reformatted_source, _ = yapf_api.FormatCode(
|
|
py3compat.unicode('\n'.join(source) + '\n'),
|
|
filename='<stdin>',
|
|
style_config=style_config,
|
|
lines=lines,
|
|
verify=args.verify)
|
|
file_resources.WriteReformattedCode('<stdout>', reformatted_source)
|
|
return 0
|
|
|
|
files = file_resources.GetCommandLineFiles(args.files, args.recursive,
|
|
args.exclude)
|
|
if not files:
|
|
raise errors.YapfError('Input filenames did not match any python files')
|
|
|
|
FormatFiles(
|
|
files,
|
|
lines,
|
|
style_config=args.style,
|
|
no_local_style=args.no_local_style,
|
|
in_place=args.in_place,
|
|
print_diff=args.diff,
|
|
verify=args.verify,
|
|
parallel=args.parallel)
|
|
return 0
|
|
|
|
|
|
def FormatFiles(filenames,
|
|
lines,
|
|
style_config=None,
|
|
no_local_style=False,
|
|
in_place=False,
|
|
print_diff=False,
|
|
verify=True,
|
|
parallel=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.
|
|
style_config: (string) Style name or file path.
|
|
no_local_style: (string) If style_config is None don't search for
|
|
directory-local style configuration.
|
|
in_place: (bool) Modify the files in place.
|
|
print_diff: (bool) Instead of returning the reformatted source, return a
|
|
diff that turns the formatted source into reformatter source.
|
|
verify: (bool) True if reformatted code should be verified for syntax.
|
|
parallel: (bool) True if should format multiple files in parallel.
|
|
|
|
Returns:
|
|
True if the source code changed in any of the files being formatted.
|
|
"""
|
|
changed = False
|
|
if parallel:
|
|
import multiprocessing # pylint: disable=g-import-not-at-top
|
|
import concurrent.futures # pylint: disable=g-import-not-at-top
|
|
workers = min(multiprocessing.cpu_count(), len(filenames))
|
|
with concurrent.futures.ProcessPoolExecutor(workers) as executor:
|
|
future_formats = [
|
|
executor.submit(_FormatFile, filename, lines, style_config,
|
|
no_local_style, in_place, print_diff, verify)
|
|
for filename in filenames
|
|
]
|
|
for future in concurrent.futures.as_completed(future_formats):
|
|
changed |= future.result()
|
|
else:
|
|
for filename in filenames:
|
|
changed |= _FormatFile(filename, lines, style_config, no_local_style,
|
|
in_place, print_diff, verify)
|
|
return changed
|
|
|
|
|
|
def _FormatFile(filename,
|
|
lines,
|
|
style_config=None,
|
|
no_local_style=False,
|
|
in_place=False,
|
|
print_diff=False,
|
|
verify=True):
|
|
logging.info('Reformatting %s', filename)
|
|
if style_config is None and not no_local_style:
|
|
style_config = (
|
|
file_resources.GetDefaultStyleForDir(os.path.dirname(filename)))
|
|
try:
|
|
reformatted_code, encoding, has_change = yapf_api.FormatFile(
|
|
filename,
|
|
in_place=in_place,
|
|
style_config=style_config,
|
|
lines=lines,
|
|
print_diff=print_diff,
|
|
verify=verify,
|
|
logger=logging.warning)
|
|
if not in_place and reformatted_code:
|
|
file_resources.WriteReformattedCode(filename, reformatted_code, in_place,
|
|
encoding)
|
|
return has_change
|
|
except SyntaxError as e:
|
|
e.filename = filename
|
|
raise
|
|
|
|
|
|
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:
|
|
# The 'list' here is needed by Python 3.
|
|
line = list(map(int, line_string.split('-', 1)))
|
|
if line[0] < 1:
|
|
raise errors.YapfError('invalid start of line range: %r' % line)
|
|
if line[0] > line[1]:
|
|
raise errors.YapfError('end comes before start in line range: %r', line)
|
|
lines.append(tuple(line))
|
|
return lines
|
|
|
|
|
|
def run_main(): # pylint: disable=invalid-name
|
|
try:
|
|
sys.exit(main(sys.argv))
|
|
except errors.YapfError as e:
|
|
sys.stderr.write('yapf: ' + str(e) + '\n')
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
run_main()
|