1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import argparse
6import lib2to3.refactor
7
8from webkitpy.common.system.systemhost import SystemHost
9from webkitpy.thirdparty import autopep8
10
11
12def parse_args(args=None):
13    parser = argparse.ArgumentParser()
14    parser.add_argument('--chromium', action='store_const', dest='style', const='chromium', default='blink',
15                        help="Format according to Chromium's Python coding styles instead of Blink's.")
16    parser.add_argument('--no-backups', action='store_false', default=True, dest='backup',
17                        help='Do not back up files before overwriting them.')
18    parser.add_argument('-j', '--jobs', metavar='n', type=int, default=0,
19                        help='Number of parallel jobs; match CPU count if less than 1.')
20    parser.add_argument('files', nargs='*', default=['-'],
21                        help="files to format or '-' for standard in")
22    parser.add_argument('--double-quote-strings', action='store_const', dest='quoting', const='double', default='single',
23                        help='Rewrite string literals to use double quotes instead of single quotes.')
24    parser.add_argument('--no-autopep8', action='store_true',
25                        help='Skip the autopep8 code-formatting step.')
26    parser.add_argument('--leave-strings-alone', action='store_true',
27                        help='Do not reformat string literals to use a consistent quote style.')
28    return parser.parse_args(args=args)
29
30
31def main(host=None, args=None):
32    options = parse_args(args)
33    if options.no_autopep8:
34        options.style = None
35
36    if options.leave_strings_alone:
37        options.quoting = None
38
39    autopep8_options = _autopep8_options_for_style(options.style)
40    fixers = _fixers_for_quoting(options.quoting)
41
42    if options.files == ['-']:
43        host = host or SystemHost()
44        host.print_(reformat_source(host.stdin.read(), autopep8_options, fixers, '<stdin>'), end='')
45        return
46
47    # We create the arglist before checking if we need to create a Host, because a
48    # real host is non-picklable and can't be passed to host.executive.map().
49
50    arglist = [(host, name, autopep8_options, fixers, options.backup) for name in options.files]
51    host = host or SystemHost()
52
53    host.executive.map(_reformat_thunk, arglist, processes=options.jobs)
54
55
56def _autopep8_options_for_style(style):
57    return {
58        None: [],
59        'blink': autopep8.parse_args(['--aggressive',
60                                      '--max-line-length', '132',
61                                      '--indent-size', '4',
62                                      '']),
63        'chromium': autopep8.parse_args(['--aggressive',
64                                         '--max-line-length', '80',
65                                         '--indent-size', '2',
66                                         '']),
67    }.get(style)
68
69
70def _fixers_for_quoting(quoting):
71    return {
72        None: [],
73        'double': ['webkitpy.formatter.fix_double_quote_strings'],
74        'single': ['webkitpy.formatter.fix_single_quote_strings'],
75    }.get(quoting)
76
77
78def _reformat_thunk(args):
79    reformat_file(*args)
80
81
82def reformat_file(host, name, autopep8_options, fixers, should_backup_file):
83    host = host or SystemHost()
84    source = host.filesystem.read_text_file(name)
85    dest = reformat_source(source, autopep8_options, fixers, name)
86    if dest != source:
87        if should_backup_file:
88            host.filesystem.write_text_file(name + '.bak', source)
89        host.filesystem.write_text_file(name, dest)
90
91
92def reformat_source(source, autopep8_options, fixers, name):
93    tmp_str = source
94
95    if autopep8_options:
96        tmp_str = autopep8.fix_code(tmp_str, autopep8_options)
97
98    if fixers:
99        tool = lib2to3.refactor.RefactoringTool(fixer_names=fixers,
100                                                explicit=fixers)
101        tmp_str = unicode(tool.refactor_string(tmp_str, name=name))
102
103    return tmp_str
104