4949This adds the declaration ``from __future__ import unicode_literals`` to the
5050top of each file, which implicitly declares all unadorned string literals to be
5151unicode strings (``unicode`` on Py2).
52+
53+ All imports
54+ -----------
55+
56+ The --all-imports option forces adding all ``__future__`` imports and ``from
57+ future import standard_library``, even if they don't seem necessary for
58+ the current state of each module. (This can simplify testing, and can
59+ reduce the need to think about Py2 compatibility when editing the code
60+ further.)
61+
5262"""
5363
5464from __future__ import (absolute_import , print_function , unicode_literals )
5767import sys
5868import logging
5969import optparse
70+ import os
6071
6172from lib2to3 .main import main , warn , StdoutRefactoringTool
6273from lib2to3 import refactor
6677 libfuturize_fix_names_stage1 ,
6778 libfuturize_fix_names_stage2 )
6879
80+ fixer_pkg = 'libfuturize.fixes'
81+
6982
7083def main (args = None ):
7184 """Main program.
7285
86+ Args:
87+ fixer_pkg: the name of a package where the fixers are located.
88+ args: optional; a list of command line arguments. If omitted,
89+ sys.argv[1:] is used.
90+
7391 Returns a suggested exit status (0, 1, 2).
7492 """
93+
7594 # Set up option parser
7695 parser = optparse .OptionParser (usage = "futurize [options] file|dir ..." )
7796 parser .add_option ("-a" , "--all-imports" , action = "store_true" ,
@@ -87,15 +106,15 @@ def main(args=None):
87106 parser .add_option ("-u" , "--unicode-literals" , action = "store_true" ,
88107 help = "Add ``from __future__ import unicode_literals`` to implicitly convert all unadorned string literals '' into unicode strings" )
89108 parser .add_option ("-f" , "--fix" , action = "append" , default = [],
90- help = "Each FIX specifies a transformation; default: all" )
109+ help = "Each FIX specifies a transformation; default: all. \n Either use '-f division -f metaclass' etc. or use the fully-qualified module name: '-f lib2to3.fixes.fix_types -f libfuturize.fixes.fix_unicode_keep_u' " )
91110 parser .add_option ("-j" , "--processes" , action = "store" , default = 1 ,
92111 type = "int" , help = "Run 2to3 concurrently" )
93112 parser .add_option ("-x" , "--nofix" , action = "append" , default = [],
94113 help = "Prevent a fixer from being run." )
95114 parser .add_option ("-l" , "--list-fixes" , action = "store_true" ,
96115 help = "List available transformations" )
97- # parser.add_option("-p", "--print-function", action="store_true",
98- # help="Modify the grammar so that print() is a function")
116+ parser .add_option ("-p" , "--print-function" , action = "store_true" ,
117+ help = "Modify the grammar so that print() is a function" )
99118 parser .add_option ("-v" , "--verbose" , action = "store_true" ,
100119 help = "More verbose logging" )
101120 parser .add_option ("--no-diffs" , action = "store_true" ,
@@ -104,13 +123,51 @@ def main(args=None):
104123 help = "Write back modified files" )
105124 parser .add_option ("-n" , "--nobackups" , action = "store_true" , default = False ,
106125 help = "Don't write backups for modified files." )
126+ parser .add_option ("-o" , "--output-dir" , action = "store" , type = "str" ,
127+ default = "" , help = "Put output files in this directory "
128+ "instead of overwriting the input files. Requires -n." )
129+ parser .add_option ("-W" , "--write-unchanged-files" , action = "store_true" ,
130+ help = "Also write files even if no changes were required"
131+ " (useful with --output-dir); implies -w." )
132+ parser .add_option ("--add-suffix" , action = "store" , type = "str" , default = "" ,
133+ help = "Append this string to all output filenames."
134+ " Requires -n if non-empty. "
135+ "ex: --add-suffix='3' will generate .py3 files." )
136+
137+ avail_fixes = set ()
107138
108139 # Parse command line arguments
109140 refactor_stdin = False
110- flags = {}
111141 options , args = parser .parse_args (args )
112- fixer_pkg = 'libfuturize.fixes'
113- avail_fixes = set ()
142+ if options .write_unchanged_files :
143+ flags ["write_unchanged_files" ] = True
144+ if not options .write :
145+ warn ("--write-unchanged-files/-W implies -w." )
146+ options .write = True
147+ # If we allowed these, the original files would be renamed to backup names
148+ # but not replaced.
149+ if options .output_dir and not options .nobackups :
150+ parser .error ("Can't use --output-dir/-o without -n." )
151+ if options .add_suffix and not options .nobackups :
152+ parser .error ("Can't use --add-suffix without -n." )
153+
154+ if not options .write and options .no_diffs :
155+ warn ("not writing files and not printing diffs; that's not very useful" )
156+ if not options .write and options .nobackups :
157+ parser .error ("Can't use -n without -w" )
158+ if "-" in args :
159+ refactor_stdin = True
160+ if options .write :
161+ print ("Can't write to stdin." , file = sys .stderr )
162+ return 2
163+ # Is this ever necessary?
164+ if options .print_function :
165+ flags ["print_function" ] = True
166+
167+ # Set up logging handler
168+ level = logging .DEBUG if options .verbose else logging .INFO
169+ logging .basicConfig (format = '%(name)s: %(message)s' , level = level )
170+
114171 if options .stage1 or options .stage2 :
115172 assert options .both_stages is None
116173 options .both_stages = False
@@ -123,16 +180,12 @@ def main(args=None):
123180 avail_fixes .update (lib2to3_fix_names_stage2 )
124181 avail_fixes .update (libfuturize_fix_names_stage2 )
125182
126- # if options.tobytes:
127- # avail_fixes.add('libfuturize.fixes.fix_bytes')
128183 if options .unicode_literals :
129184 avail_fixes .add ('libfuturize.fixes.fix_unicode_literals_import' )
130- if not options .write and options .no_diffs :
131- warn ("not writing files and not printing diffs; that's not very useful" )
132- if not options .write and options .nobackups :
133- parser .error ("Can't use -n without -w" )
185+
134186 if options .list_fixes :
135187 print ("Available transformations for the -f/--fix option:" )
188+ # for fixname in sorted(refactor.get_all_fix_names(fixer_pkg)):
136189 for fixname in sorted (avail_fixes ):
137190 print (fixname )
138191 if not args :
@@ -142,24 +195,12 @@ def main(args=None):
142195 file = sys .stderr )
143196 print ("Use --help to show usage." , file = sys .stderr )
144197 return 2
145- if "-" in args :
146- refactor_stdin = True
147- if options .write :
148- print ("Can't write to stdin." , file = sys .stderr )
149- return 2
150198
151- # Is this ever necessary?
152- # if options.print_function:
153- # flags["print_function"] = True
154-
155- # Set up logging handler
156- level = logging .DEBUG if options .verbose else logging .INFO
157- logging .basicConfig (format = '%(name)s: %(message)s' , level = level )
199+ flags = {}
158200
159- # Initialize the refactoring tool
160201 unwanted_fixes = set (fixer_pkg + ".fix_" + fix for fix in options .nofix )
161202
162- # The 'all-imports' option forces adding all imports __future__ and "from
203+ # The 'all-imports' option forces adding all __future__ imports and "from
163204 # future import standard_library", even if they don't seem necessary for
164205 # the current state of each module. (This can simplify testing, and can
165206 # reduce the need to think about Py2 compatibility when editing the code
@@ -175,11 +216,51 @@ def main(args=None):
175216 extra_fixes .add (prefix + 'fix_add__future__imports' )
176217 extra_fixes .add (prefix + 'fix_add_future_standard_library_import' )
177218 extra_fixes .add (prefix + 'fix_add_all_future_builtins' )
219+ explicit = set ()
220+ if options .fix :
221+ all_present = False
222+ for fix in options .fix :
223+ if fix == 'all' :
224+ all_present = True
225+ else :
226+ if ".fix_" in fix :
227+ explicit .add (fix )
228+ else :
229+ # Infer the full module name for the fixer.
230+ # First ensure that no names clash (e.g.
231+ # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah):
232+ found = [f for f in avail_fixes if 'fix_{}' .format (fix ) in f ]
233+ if len (found ) > 1 :
234+ print ("Ambiguous fixer name. Choose a fully qualified "
235+ "module name instead from these:\n " +
236+ "\n " .join (" " + myf for myf in found ),
237+ file = sys .stderr )
238+ return 2
239+ explicit .add (found [0 ])
240+ requested = avail_fixes .union (explicit ) if all_present else explicit
241+ else :
242+ requested = avail_fixes .union (explicit )
243+ fixer_names = requested | extra_fixes - unwanted_fixes
244+
245+ input_base_dir = os .path .commonprefix (args )
246+ if (input_base_dir and not input_base_dir .endswith (os .sep )
247+ and not os .path .isdir (input_base_dir )):
248+ # One or more similar names were passed, their directory is the base.
249+ # os.path.commonprefix() is ignorant of path elements, this corrects
250+ # for that weird API.
251+ input_base_dir = os .path .dirname (input_base_dir )
252+ if options .output_dir :
253+ input_base_dir = input_base_dir .rstrip (os .sep )
254+ logger .info ('Output in %r will mirror the input directory %r layout.' ,
255+ options .output_dir , input_base_dir )
178256
179- fixer_names = avail_fixes | extra_fixes - unwanted_fixes
180-
181- rt = StdoutRefactoringTool (sorted (fixer_names ), flags , set (),
182- options .nobackups , not options .no_diffs )
257+ # Initialize the refactoring tool
258+ rt = StdoutRefactoringTool (
259+ sorted (fixer_names ), flags , sorted (explicit ),
260+ options .nobackups , not options .no_diffs ,
261+ input_base_dir = input_base_dir ,
262+ output_dir = options .output_dir ,
263+ append_suffix = options .add_suffix )
183264
184265 # Refactor all files and directories passed as arguments
185266 if not rt .errors :
@@ -198,4 +279,3 @@ def main(args=None):
198279
199280 # Return error status (0 if rt.errors is zero)
200281 return int (bool (rt .errors ))
201-
0 commit comments