# Copyright (C) 2009 Daniel Harding # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. r"""Executes a Python script on Windows, using heuristics to determine which Python interpreter to use. If the specified script doesn't start with #!, executes the script using the same interpreter that is running this script (i.e. sys.executable). If the the script does start with #!, attempts to determine which Python interpreter to use based on the contents of the first line of the script. Examples: #! C:\Python26\python.exe uses the interpreter specified on the #! line - in this case C:\Python26\python.exe #! /usr/bin/env python uses the same interpreter that is running this script (i.e. sys.executable) #! /usr/bin/env python3 searches the Windows registry for the latest installed version of Python 3.x, and uses the interpreter from that installation #! /usr/bin/env python25 looks in the Windows registry to determine the installation path of Python 2.5 and uses the interpreter from that installation #! /usr/bin/env python2.5 same as the previous example If there are arguments specified on the #! line, they are passed to the Python interpreter along with the script plus any arguments for the script itself. """ import re import os import os.path import shlex import sys try: from optparse import OptionParser except ImportError: sys.stdout.write("Error: %s requires Python 2.3 or later\n" % os.path.basename(sys.argv[0])) sys.exit(1) try: import _winreg as winreg except ImportError: import winreg ERROR_FILE_NOT_FOUND = 2 class RunnerException(Exception): pass # utility function because of differences between Python 2.x and Python 3.x # exception handling syntax def get_current_exception(): return sys.exc_info()[1] def set_registry_entry(file_type, command, icon_file, user_only): if user_only: file_type_key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, r'Software\CLASSES\%s' % file_type) else: file_type_key = winreg.CreateKey(winreg.HKEY_CLASSES_ROOT, file_type) try: command_key = winreg.CreateKey(file_type_key, r'shell\open\command') try: winreg.SetValue(command_key, None, winreg.REG_SZ, command) finally: winreg.CloseKey(command_key) if icon_file is not None: icon_key = winreg.CreateKey(file_type_key, 'DefaultIcon') try: winreg.SetValue(icon_key, None, winreg.REG_SZ, icon_file) finally: winreg.CloseKey(icon_key) else: try: winreg.DeleteKey(file_type_key, 'DefaultIcon') except WindowsError: # if the DefaultIcon key doesn't exist, ignore the error when # trying to delete it if get_current_exception().errno != ERROR_FILE_NOT_FOUND: raise finally: winreg.CloseKey(file_type_key) def find_icon_file(python_dir, file_name): icon_path = os.path.join(python_dir, file_name) if not os.path.exists(icon_path): icon_path = os.path.join(python_dir, 'DLLs', file_name) if not os.path.exists(icon_path): icon_path = None return icon_path def substitute(source, substitutions): for old, new in substitutions: source = source.replace(old, new) return source def set_registry_entries(python_file_command, python_noconfile_command, python_compiledfile_command, user_only): try: python_dir = os.path.dirname(sys.executable) py_icon_path = find_icon_file(python_dir, 'py.ico') if py_icon_path is None: sys.stderr.write('Warning: Could not find icon for python' ' files\n') pyc_icon_path = find_icon_file(python_dir, 'pyc.ico') if pyc_icon_path is None: sys.stderr.write('Warning: Could not find icon for compiled' ' python files\n') substitutions = ( ('${python_exe}', os.path.join(python_dir, 'python.exe')), ('${pythonw_exe}', os.path.join(python_dir, 'pythonw.exe')), ) set_registry_entry('Python.File', substitute(python_file_command, substitutions), py_icon_path, user_only) set_registry_entry('Python.NoConFile', substitute(python_noconfile_command, substitutions), py_icon_path, user_only) set_registry_entry('Python.CompiledFile', substitute(python_compiledfile_command, substitutions), pyc_icon_path, user_only) except WindowsError: raise RunnerException('Could not write to the Windows registry: %s' % get_current_exception()) def install(user_only): script_path = os.path.normpath(os.path.abspath(sys.argv[0])) set_registry_entries( python_file_command='"${python_exe}" "%s" "%%1" %%*' % script_path, python_noconfile_command='"${pythonw_exe}" "%s" "%%1" %%*' % script_path, python_compiledfile_command='"${python_exe}" "%1" %*', user_only=user_only) def uninstall(user_only): set_registry_entries( python_file_command='"${python_exe}" "%1" %*', python_noconfile_command='"${pythonw_exe}" "%1" %*', python_compiledfile_command='"${python_exe}" "%1" %*', user_only=user_only) def get_interpreter_name(script): extension = os.path.splitext(script)[1] if extension == '.pyw': executable = 'pythonw.exe' else: executable = 'python.exe' return executable def get_interpreter(path, script): return os.path.join(path, get_interpreter_name(script)) def get_default_interpreter(script): return get_interpreter(os.path.dirname(sys.executable), script) def get_major_version_install_path(python_keys, major_version): install_path = None best_version = (0, 0) for python_key in python_keys: subkey_count = winreg.QueryInfoKey(python_key)[0] for i in range(subkey_count): version_key = winreg.EnumKey(python_key, i) if re.match(r'^\d\.\d$', version_key) == None: sys.stderr.write("Warning: Ignoring incorrectly formatted" " version key '%s'.\n" % version_key) continue registry_version = tuple(map(int, version_key.split('.'))) if registry_version[0] == major_version \ and registry_version> best_version: try: install_path = winreg.QueryValue(python_key, '%s\InstallPath' % version_key) except WindowsError: # sometimes the registry key for a version of Python will # still exist in the registry even after that version of # Python has been uninstalled, so ignore versions that # don't have an InstallPath subkey if get_current_exception().errno != ERROR_FILE_NOT_FOUND: raise else: best_version = registry_version if install_path is None: raise RunnerException('Could not locate any installations for' ' Python %d.x using the Windows registry.' % major_version) return install_path def get_specific_version_install_path(python_keys, version): install_path = None registry_path = '%s\InstallPath' % '.'.join(map(str, version)) for python_key in python_keys: try: install_path = winreg.QueryValue(python_key, registry_path) except WindowsError: if get_current_exception().errno != ERROR_FILE_NOT_FOUND: raise else: break if install_path is None: raise RunnerException('Could not find an installation of Python %d.%d' ' using the Windows registry.' % version) return install_path def find_best_interpreter(version, script): try: python_keys = [] for key in (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE): try: python_keys.append(winreg.OpenKey(key, 'SOFTWARE\Python\PythonCore')) except WindowsError: if get_current_exception().errno != ERROR_FILE_NOT_FOUND: raise if not python_keys: raise RunnerException('Could not locate any Python installations' ' using the Windows registry.') try: if len(version) == 1: install_path = get_major_version_install_path(python_keys, version[0]) else: install_path = get_specific_version_install_path(python_keys, version) return get_interpreter(install_path, script) finally: for python_key in python_keys: winreg.CloseKey(python_key) except WindowsError: raise RunnerException('Could not read from the Windows registry: %s' % get_current_exception()) # escape arguments so that they will be parsed properly by Microsoft C/C++ # startup code as documented at # def escape_args(args): new_args = [] for arg in args: # escape quotation marks, doubling up any preceeding backslashes arg = re.sub(r'(\\*)"', r'1円1円\\"', arg) # wrap string in quotes if it contains spaces or tabs, doubling up # backslashes that occur at the end of the argument if ' ' in arg or '\t' in arg: arg = '"%s"' % re.sub(r'(\\+)$', r'1円1円', arg) new_args.append(arg) return new_args def execute_script(python, python_args, script, script_args): try: # must use os.spawnv with os.P_WAIT because on windows os.execv does # not actually overlay the current process as it does on POSIX # platforms - this results in two instances in the Python interpreter # in memory, but it can't be helped sys.exit(os.spawnv(os.P_WAIT, python, escape_args([python] + python_args + [script] + script_args))) except OSError: raise RunnerException('Could not start Python excutable "%s": %s' % (python, get_current_exception())) def run_script(script, script_args): python = get_default_interpreter(script) python_args = [] try: f = open(script, 'r') line = f.readline() f.close() except IOError: raise RunnerException('Could not read from script "%s": %s' % (script, get_current_exception())) if line.startswith('#!'): line = line[2:] parts = shlex.split(line) if not parts: raise RunnerException('Script "%s" starts with \'#!\' but does not' ' specify an executable.' % script) executable = parts[0] if os.path.exists(executable): if not os.path.isabs(executable): raise RunnerException('Script "%s" specifies an executable' 'using a relative path (%s).' % (script, executable)) python = executable python_args = parts[1:] else: # match the following forms (where X and Y are major and minor # version numbers, respectively): # python # pythonX # pythonXY # pythonX.Y m = re.search(r'\bpython((?:\d\.\d)|\d{,2})\b', line) if m: python_args = shlex.split(line[m.end():]) version = tuple(map(int, m.group(1).replace('.', ''))) if version: python = find_best_interpreter(version, script) else: raise RunnerException('Script "%s" starts with \'#!\' but does' 'not specify a Python executable to use.') execute_script(python, python_args, script, script_args) def main(): try: parser = OptionParser() parser.set_usage("""usage: python %prog script args python %prog --install [options] python %prog --uninstall [options]""") parser.add_option('--install', action='store_true', help='update the Windows registry to use this script to run' ' Python files, defaulting to the current Python' ' interpreter (Python %d.%d)' % sys.version_info[0:2]) parser.add_option('--uninstall', action='store_true', default=False, help='update the Windows registry to use the current Python' ' interpreter (Python %d.%d) to run Python files' % sys.version_info[0:2]) parser.add_option('--all', action='store_false', dest='user_only', help='update the Windows registry for all users (requires' ' administrative rights) (default)') parser.add_option('--user', action='store_true', dest='user_only', help='update the Windows registry for only for the current' ' user') parser.set_defaults(install=False, uninstall=False, user_only=None) parser.disable_interspersed_args() options, args = parser.parse_args() if options.install: if options.uninstall: parser.error('--install and --uninstall are mutually' ' exclusive') if args: parser.error('--install cannot be specified along with a' ' script') install(options.user_only) elif options.uninstall: if args: parser.error('--uninstall cannot be specified along with a' ' script') uninstall(options.user_only) else: if not args: parser.error('script required') if options.user_only is True: parser.error('--user cannot be specified along with a script') if options.user_only is False: parser.error('--all cannot be specified along with a script') run_script(args[0], args[1:]) except RunnerException: sys.stderr.write('Error: %s' % get_current_exception()) sys.exit(1) if __name__ == '__main__': main()

AltStyle によって変換されたページ (->オリジナル) /