2323__author__ = 'Wouter van Oortmerssen'
2424
2525import argparse
26+ import ctypes
2627import json
2728import os
29+ import platform
2830import sys
2931from xml .etree import ElementTree
3032
@@ -218,6 +220,33 @@ def indent(elem, level=0):
218220 elem .tail = i
219221
220222
223+ def argv_as_unicode_win32 ():
224+ """Returns unicode command line arguments on windows.
225+ """
226+
227+ get_command_line_w = ctypes .cdll .kernel32 .GetCommandLineW
228+ get_command_line_w .restype = ctypes .wintypes .LPCWSTR
229+
230+ # CommandLineToArgvW parses the Unicode command line
231+ command_line_to_argv_w = ctypes .windll .shell32 .CommandLineToArgvW
232+ command_line_to_argv_w .argtypes = [
233+ ctypes .wintypes .LPCWSTR ,
234+ ctypes .wintypes .POINTER (ctypes .wintypes .c_int )
235+ ]
236+ command_line_to_argv_w .restype = ctypes .wintypes .POINTER (
237+ ctypes .wintypes .LPWSTR )
238+
239+ argc = ctypes .wintypes .c_int (0 )
240+ argv = command_line_to_argv_w (get_command_line_w (), argc )
241+
242+ # Strip the python executable from the arguments if it exists
243+ # (It would be listed as the first argument on the windows command line, but
244+ # not in the arguments to the python script)
245+ sys_argv_len = len (sys .argv )
246+ return [unicode (argv [i ]) for i in
247+ range (argc .value - sys_argv_len , argc .value )]
248+
249+
221250def main ():
222251 parser = argparse .ArgumentParser (
223252 description = ((
@@ -254,6 +283,11 @@ def main():
254283 default = False ,
255284 required = False )
256285
286+ # python 2 on Windows doesn't handle unicode arguments well, so we need to
287+ # pre-process the command line arguments before trying to parse them.
288+ if platform .system () == 'Windows' :
289+ sys .argv = argv_as_unicode_win32 ()
290+
257291 args = parser .parse_args ()
258292
259293 if args .plist :
@@ -264,12 +298,18 @@ def main():
264298 output_filename = DEFAULT_OUTPUT_FILENAME
265299
266300 if args .i :
267- input_filename = args .i
301+ input_filename_raw = args .i
302+ # Encode the input string (type unicode) as a normal string (type str)
303+ # using the 'utf-8' encoding so that it can be worked with the same as
304+ # input names from other sources (like the defaults).
305+ input_filename = input_filename_raw .encode ('utf-8' )
268306
269307 if args .o :
270308 output_filename = args .o
271309
272- with open (input_filename , 'r' ) as ifile :
310+ # Decode the filename to a unicode string using the 'utf-8' encoding to
311+ # properly handle filepaths with unicode characters in them.
312+ with open (input_filename .decode ('utf-8' ), 'r' ) as ifile :
273313 file_string = ifile .read ()
274314
275315 json_string = None
@@ -387,7 +427,9 @@ def main():
387427 if args .l :
388428 for package in packages :
389429 if package :
390- sys .stdout .write (package + '\n ' )
430+ # Encode the output string in case the system's default encoding differs
431+ # from the encoding of the string being printed.
432+ sys .stdout .write ((package + '\n ' ).encode (sys .getdefaultencoding ()))
391433 else :
392434 path = os .path .dirname (output_filename )
393435
0 commit comments