diff -r 10eaab5887cd Lib/shutil.py --- a/Lib/shutil.py Mon Apr 23 14:25:19 2012 +0800 +++ b/Lib/shutil.py Tue Apr 24 04:40:00 2012 -0700 @@ -921,3 +921,82 @@ lines = size.lines return os.terminal_size((columns, lines)) + + import sys, os, subprocess, errno + + class NoAssociatedApplicationError(EnvironmentError): + """The OS has no application association for files/directories of the given type.""" + + # def launch(path): + # FIXME: confirm the type of path on the various platforms + """Open the given path with the application that the OS associates with the path's file type. + + :param path: the path to the file or directory to open + :type path: On Windows: :class:`str`. On Unix and Mac OS X: :class:`bytes`. + + On Windows, calls :func:`os.startfile`. + + On Mac OS X, uses the ```open `_`` command. + + On non-Mac Unix, uses the ```xdg-open `_`` command and fails + if it can't be run (e.g., when it's not installed). + + :raises NotImplementedError: on non-Mac Unix when ``xdg-open`` itself cannot be run + :raises FileNotFoundError: when *path* doesn't exist + :raises NoAssociatedApplicationError: when the OS has no application association for files/directories of *path*'s type + :raises OSError, IOError, subprocess.CalledProcessError: when lower-level implementation APIs encounter errors + :returns: None + + Availability: Mac OS X, Unix, Windows + + Example: + >>> launch('README') + """ + + if sys.platform.startswith('win32'): + def launch(path): + path = os.path.normpath(path) # Ensure cross-platform path (Windows-friendly) + os.startfile(path) + + elif sys.platform.startswith('darwin'): + def launch(path): + path = os.path.normpath(path) # Ensure cross-platform path + # https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man1/open.1.html + cmd = ['open', path] + proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL) + _, stderr_output = proc.communicate() + if proc.returncode: + if stderr_output.endswith("does not exist."): + raise FileNotFoundError("Path %s may not exist" % repr(path)) from err + elif stderr_output.startswith("No application can open"): + raise NoAssociatedApplicationError("No application is associated with files/directories of the given type") from err + else: + raise subprocess.CalledProcessError(proc.returncode, cmd, stderr_output) + + elif os.name == 'posix': + def launch(path): + path = os.path.normpath(path) # Ensure cross-platform path + # http://portland.freedesktop.org/xdg-utils-1.0/xdg-open.html + try: + subprocess.check_call(['xdg-open', path], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError as err: + if err.returncode == 2: + raise FileNotFoundError("Path %s may not exist" % repr(path)) from err + elif err.returncode == 3: + raise NoAssociatedApplicationError("No application is associated with files/directories of the given type") from err + else: + raise + except EnvironmentError as err: + if err.errno == errno.ENOENT: # xdg-open not installed/accessible + raise NotImplementedError from err + else: + raise +