18951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson"""
28951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin PetersonMain program for 2to3.
38951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson"""
48951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
52acbae80165390a5be3bb12351147b8026787e13Zachary Warefrom __future__ import with_statement, print_function
68d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson
78951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Petersonimport sys
88951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Petersonimport os
93059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Petersonimport difflib
108951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Petersonimport logging
110b24b3d9511f8f838da4dfb9a59255aff5fcf72eBenjamin Petersonimport shutil
128951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Petersonimport optparse
138951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
148951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Petersonfrom . import refactor
158951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
163059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson
173059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Petersondef diff_texts(a, b, filename):
183059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson    """Return a unified diff of two strings."""
193059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson    a = a.splitlines()
203059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson    b = b.splitlines()
213059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson    return difflib.unified_diff(a, b, filename, filename,
223059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson                                "(original)", "(refactored)",
233059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson                                lineterm="")
243059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson
253059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson
26608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Petersonclass StdoutRefactoringTool(refactor.MultiprocessRefactoringTool):
27d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson    """
2858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    A refactoring tool that can avoid overwriting its input files.
29d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson    Prints output to stdout.
3058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith
3158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    Output files can optionally be written to a different directory and or
3258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    have an extra file suffix appended to their name for use in situations
3358f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    where you do not want to replace the input files.
34d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson    """
35d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson
3658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    def __init__(self, fixers, options, explicit, nobackups, show_diffs,
3758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                 input_base_dir='', output_dir='', append_suffix=''):
3858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        """
3958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        Args:
4058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            fixers: A list of fixers to import.
4158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            options: A dict with RefactoringTool configuration.
4258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            explicit: A list of fixers to run even if they are explicit.
4358f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            nobackups: If true no backup '.bak' files will be created for those
4458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                files that are being refactored.
4558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            show_diffs: Should diffs of the refactoring be printed to stdout?
4658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            input_base_dir: The base directory for all input files.  This class
4758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                will strip this path prefix off of filenames before substituting
4858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                it with output_dir.  Only meaningful if output_dir is supplied.
4958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                All files processed by refactor() must start with this path.
5058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            output_dir: If supplied, all converted files will be written into
5158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                this directory tree instead of input_base_dir.
5258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            append_suffix: If supplied, all files output by this tool will have
5358f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                this appended to their filename.  Useful for changing .py to
5458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                .py3 for example by passing append_suffix='3'.
5558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        """
56206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        self.nobackups = nobackups
573059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson        self.show_diffs = show_diffs
5858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        if input_base_dir and not input_base_dir.endswith(os.sep):
5958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            input_base_dir += os.sep
6058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        self._input_base_dir = input_base_dir
6158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        self._output_dir = output_dir
6258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        self._append_suffix = append_suffix
63206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        super(StdoutRefactoringTool, self).__init__(fixers, options, explicit)
64206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson
65d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson    def log_error(self, msg, *args, **kwargs):
66d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson        self.errors.append((msg, args, kwargs))
67d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson        self.logger.error(msg, *args, **kwargs)
68d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson
69d481e3d7914d20238c62c76991255b3b2b5e4a17Benjamin Peterson    def write_file(self, new_text, filename, old_text, encoding):
7058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        orig_filename = filename
7158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        if self._output_dir:
7258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            if filename.startswith(self._input_base_dir):
7358f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                filename = os.path.join(self._output_dir,
7458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                                        filename[len(self._input_base_dir):])
7558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            else:
7658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                raise ValueError('filename %s does not start with the '
7758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                                 'input_base_dir %s' % (
7858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                                         filename, self._input_base_dir))
7958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        if self._append_suffix:
8058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            filename += self._append_suffix
8158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        if orig_filename != filename:
82efc66f9e365be38ffb3db5252a10170df8a947acGregory P. Smith            output_dir = os.path.dirname(filename)
83efc66f9e365be38ffb3db5252a10170df8a947acGregory P. Smith            if not os.path.isdir(output_dir):
84efc66f9e365be38ffb3db5252a10170df8a947acGregory P. Smith                os.makedirs(output_dir)
85efc66f9e365be38ffb3db5252a10170df8a947acGregory P. Smith            self.log_message('Writing converted %s to %s.', orig_filename,
86efc66f9e365be38ffb3db5252a10170df8a947acGregory P. Smith                             filename)
87206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        if not self.nobackups:
88206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson            # Make backup
89206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson            backup = filename + ".bak"
90206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson            if os.path.lexists(backup):
91206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                try:
92206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                    os.remove(backup)
93ad28c7f9dad791567afa0624acfb3ba430851965Andrew Svetlov                except OSError as err:
94206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                    self.log_message("Can't remove backup %s", backup)
95206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson            try:
96206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                os.rename(filename, backup)
97ad28c7f9dad791567afa0624acfb3ba430851965Andrew Svetlov            except OSError as err:
98206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                self.log_message("Can't rename %s to %s", filename, backup)
99206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        # Actually write the new file
100d481e3d7914d20238c62c76991255b3b2b5e4a17Benjamin Peterson        write = super(StdoutRefactoringTool, self).write_file
101d481e3d7914d20238c62c76991255b3b2b5e4a17Benjamin Peterson        write(new_text, filename, old_text, encoding)
1029203501bae35a7a6fbec2287324382075177872dBenjamin Peterson        if not self.nobackups:
1038bcddcabd770dd424b97d7c667ef8e5337436215Benjamin Peterson            shutil.copymode(backup, filename)
10458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        if orig_filename != filename:
10558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            # Preserve the file mode in the new output directory.
10658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            shutil.copymode(orig_filename, filename)
107206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson
1083059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson    def print_output(self, old, new, filename, equal):
1093059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson        if equal:
1103059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson            self.log_message("No changes to %s", filename)
1113059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson        else:
1123059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson            self.log_message("Refactored %s", filename)
1133059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson            if self.show_diffs:
114ffa94b0f022a697dd491a0c31acd2e3b2629c048Benjamin Peterson                diff_lines = diff_texts(old, new, filename)
115ffa94b0f022a697dd491a0c31acd2e3b2629c048Benjamin Peterson                try:
1168d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                    if self.output_lock is not None:
1178d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                        with self.output_lock:
1188d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                            for line in diff_lines:
1198d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                                print(line)
1208d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                            sys.stdout.flush()
1218d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                    else:
1228d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                        for line in diff_lines:
1238d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                            print(line)
124ffa94b0f022a697dd491a0c31acd2e3b2629c048Benjamin Peterson                except UnicodeEncodeError:
125ffa94b0f022a697dd491a0c31acd2e3b2629c048Benjamin Peterson                    warn("couldn't encode %s's diff for your terminal" %
126ffa94b0f022a697dd491a0c31acd2e3b2629c048Benjamin Peterson                         (filename,))
127ffa94b0f022a697dd491a0c31acd2e3b2629c048Benjamin Peterson                    return
1283059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson
1293059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Petersondef warn(msg):
1308fb00bef2747b832c828981f2b54d2f88495694fBenjamin Peterson    print("WARNING: %s" % (msg,), file=sys.stderr)
131d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson
132d61de7f18db0eb9763a046f547053ccc59f2bfc5Benjamin Peterson
1338951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Petersondef main(fixer_pkg, args=None):
1348951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    """Main program.
1358951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
1368951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    Args:
1378951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        fixer_pkg: the name of a package where the fixers are located.
1388951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        args: optional; a list of command line arguments. If omitted,
1398951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson              sys.argv[1:] is used.
1408951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
1418951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    Returns a suggested exit status (0, 1, 2).
1428951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    """
1438951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    # Set up option parser
1440b24b3d9511f8f838da4dfb9a59255aff5fcf72eBenjamin Peterson    parser = optparse.OptionParser(usage="2to3 [options] file|dir ...")
1458951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    parser.add_option("-d", "--doctests_only", action="store_true",
1468951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson                      help="Fix up doctests only")
1478951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    parser.add_option("-f", "--fix", action="append", default=[],
148206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                      help="Each FIX specifies a transformation; default: all")
149608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson    parser.add_option("-j", "--processes", action="store", default=1,
150608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson                      type="int", help="Run 2to3 concurrently")
151206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson    parser.add_option("-x", "--nofix", action="append", default=[],
152e2bb4eb77b24a1870bd7feb15124b7adf1460efeMartin v. Löwis                      help="Prevent a transformation from being run")
1538951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    parser.add_option("-l", "--list-fixes", action="store_true",
1548d26b0be6dba383c8aa8c9496da9eccff630238eBenjamin Peterson                      help="List available transformations")
1558951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    parser.add_option("-p", "--print-function", action="store_true",
1562021100d6daec7ed40f9a47c97184d3fe708ed8dBenjamin Peterson                      help="Modify the grammar so that print() is a function")
1578951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    parser.add_option("-v", "--verbose", action="store_true",
1588951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson                      help="More verbose logging")
1593059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson    parser.add_option("--no-diffs", action="store_true",
1603059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson                      help="Don't show diffs of the refactoring")
1618951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    parser.add_option("-w", "--write", action="store_true",
1628951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson                      help="Write back modified files")
163206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson    parser.add_option("-n", "--nobackups", action="store_true", default=False,
164e2bb4eb77b24a1870bd7feb15124b7adf1460efeMartin v. Löwis                      help="Don't write backups for modified files")
16558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    parser.add_option("-o", "--output-dir", action="store", type="str",
16658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                      default="", help="Put output files in this directory "
16758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                      "instead of overwriting the input files.  Requires -n.")
16858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    parser.add_option("-W", "--write-unchanged-files", action="store_true",
16958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                      help="Also write files even if no changes were required"
17058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                      " (useful with --output-dir); implies -w.")
17158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    parser.add_option("--add-suffix", action="store", type="str", default="",
17258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                      help="Append this string to all output filenames."
17358f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                      " Requires -n if non-empty.  "
17458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                      "ex: --add-suffix='3' will generate .py3 files.")
1758951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
1768951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    # Parse command line arguments
1778951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    refactor_stdin = False
1782021100d6daec7ed40f9a47c97184d3fe708ed8dBenjamin Peterson    flags = {}
1798951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    options, args = parser.parse_args(args)
18058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    if options.write_unchanged_files:
18158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        flags["write_unchanged_files"] = True
18258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        if not options.write:
18358f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            warn("--write-unchanged-files/-W implies -w.")
18458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        options.write = True
18558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    # If we allowed these, the original files would be renamed to backup names
18658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    # but not replaced.
18758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    if options.output_dir and not options.nobackups:
18858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        parser.error("Can't use --output-dir/-o without -n.")
18958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    if options.add_suffix and not options.nobackups:
19058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        parser.error("Can't use --add-suffix without -n.")
19158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith
1923059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson    if not options.write and options.no_diffs:
1933059b00f65f8b01aa7b214b7030783c748ecc70cBenjamin Peterson        warn("not writing files and not printing diffs; that's not very useful")
194206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson    if not options.write and options.nobackups:
195206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        parser.error("Can't use -n without -w")
1968951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    if options.list_fixes:
197c0a40ab6237ffea6feabb602853ba16aae9471f4Benjamin Peterson        print("Available transformations for the -f/--fix option:")
1988951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        for fixname in refactor.get_all_fix_names(fixer_pkg):
199c0a40ab6237ffea6feabb602853ba16aae9471f4Benjamin Peterson            print(fixname)
2008951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        if not args:
2018951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson            return 0
2028951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    if not args:
203c0a40ab6237ffea6feabb602853ba16aae9471f4Benjamin Peterson        print("At least one file or directory argument required.", file=sys.stderr)
204c0a40ab6237ffea6feabb602853ba16aae9471f4Benjamin Peterson        print("Use --help to show usage.", file=sys.stderr)
2058951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        return 2
2068951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    if "-" in args:
2078951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        refactor_stdin = True
2088951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        if options.write:
209c0a40ab6237ffea6feabb602853ba16aae9471f4Benjamin Peterson            print("Can't write to stdin.", file=sys.stderr)
2108951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson            return 2
2112021100d6daec7ed40f9a47c97184d3fe708ed8dBenjamin Peterson    if options.print_function:
2122021100d6daec7ed40f9a47c97184d3fe708ed8dBenjamin Peterson        flags["print_function"] = True
2138951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
2148951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    # Set up logging handler
2158951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    level = logging.DEBUG if options.verbose else logging.INFO
2168951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    logging.basicConfig(format='%(name)s: %(message)s', level=level)
21758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    logger = logging.getLogger('lib2to3.main')
2188951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
2198951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    # Initialize the refactoring tool
220206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson    avail_fixes = set(refactor.get_fixers_from_package(fixer_pkg))
221206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson    unwanted_fixes = set(fixer_pkg + ".fix_" + fix for fix in options.nofix)
222206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson    explicit = set()
2238951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    if options.fix:
224206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        all_present = False
225206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        for fix in options.fix:
226206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson            if fix == "all":
227206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                all_present = True
228206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson            else:
229206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson                explicit.add(fixer_pkg + ".fix_" + fix)
230206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        requested = avail_fixes.union(explicit) if all_present else explicit
2318951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    else:
232206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson        requested = avail_fixes.union(explicit)
233206e3074d34aeb5a4d0c1e24d970b6569f7ad702Benjamin Peterson    fixer_names = requested.difference(unwanted_fixes)
23458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    input_base_dir = os.path.commonprefix(args)
23558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    if (input_base_dir and not input_base_dir.endswith(os.sep)
23658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        and not os.path.isdir(input_base_dir)):
23758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        # One or more similar names were passed, their directory is the base.
23858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        # os.path.commonprefix() is ignorant of path elements, this corrects
23958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        # for that weird API.
24058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        input_base_dir = os.path.dirname(input_base_dir)
24158f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    if options.output_dir:
24258f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        input_base_dir = input_base_dir.rstrip(os.sep)
24358f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith        logger.info('Output in %r will mirror the input directory %r layout.',
24458f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith                    options.output_dir, input_base_dir)
24558f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith    rt = StdoutRefactoringTool(
24658f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            sorted(fixer_names), flags, sorted(explicit),
24758f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            options.nobackups, not options.no_diffs,
24858f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            input_base_dir=input_base_dir,
24958f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            output_dir=options.output_dir,
25058f23ffb21b0fadb8df91c7c9735401f50b3e844Gregory P. Smith            append_suffix=options.add_suffix)
2518951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
2528951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    # Refactor all files and directories passed as arguments
2538951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    if not rt.errors:
2548951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        if refactor_stdin:
2558951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson            rt.refactor_stdin()
2568951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        else:
257608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson            try:
258608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson                rt.refactor(args, options.write, options.doctests_only,
259608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson                            options.processes)
260608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson            except refactor.MultiprocessingUnsupported:
261608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson                assert options.processes > 1
2628fb00bef2747b832c828981f2b54d2f88495694fBenjamin Peterson                print("Sorry, -j isn't supported on this platform.",
2638fb00bef2747b832c828981f2b54d2f88495694fBenjamin Peterson                      file=sys.stderr)
264608d8bcdfc1762cde31efdf02a6379a06d7964b1Benjamin Peterson                return 1
2658951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson        rt.summarize()
2668951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson
2678951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    # Return error status (0 if rt.errors is zero)
2688951b6172cdfcbc6c14949c4b9fa32908615d49cBenjamin Peterson    return int(bool(rt.errors))
269