2 # -*- coding: utf-8 -*-
4 """Utility for opening a file using the default application in a cross-platform
5 manner. Modified from http://code.activestate.com/recipes/511443/.
20 class BaseController(object):
21 """Base class for open program controllers."""
23 def __init__(self
, name
):
26 def open(self
, filename
):
27 raise NotImplementedError
30 class Controller(BaseController
):
31 """Controller for a generic open program."""
33 def __init__(self
, *args
):
34 super(Controller
, self
).__init
__(os
.path
.basename(args
[0]))
35 self
.args
= list(args
)
37 def _invoke(self
, cmdline
):
38 if sys
.platform
[:3] == "win":
40 startupinfo
= subprocess
.STARTUPINFO()
41 startupinfo
.dwFlags |
= subprocess
.STARTF_USESHOWWINDOW
47 os
.environ
.get("DISPLAY")
48 or sys
.platform
[:3] == "win"
49 or sys
.platform
== "darwin"
51 inout
= file(os
.devnull
, "r+")
53 # for TTY programs, we need stdin/out
56 # if possible, put the child precess in separate process group,
57 # so keyboard interrupts don't affect child precess as well as
59 setsid
= getattr(os
, "setsid", None)
61 setsid
= getattr(os
, "setpgrp", None)
63 pipe
= subprocess
.Popen(
70 startupinfo
=startupinfo
,
73 # It is assumed that this kind of tools (gnome-open, kfmclient,
74 # exo-open, xdg-open and open for OSX) immediately exit after launching
75 # the specific application
76 returncode
= pipe
.wait()
77 if hasattr(self
, "fixreturncode"):
78 returncode
= self
.fixreturncode(returncode
)
81 def open(self
, filename
):
82 if isinstance(filename
, basestring
):
83 cmdline
= self
.args
+ [filename
]
85 # assume it is a sequence
86 cmdline
= self
.args
+ filename
88 return self
._invoke
(cmdline
)
93 # Platform support for Windows
94 if sys
.platform
[:3] == "win":
96 class Start(BaseController
):
97 """Controller for the win32 start program through os.startfile."""
99 def open(self
, filename
):
101 os
.startfile(filename
)
103 # [Error 22] No application is associated with the specified
104 # file for this operation: '<URL>'
109 _controllers
["windows-default"] = Start("start")
110 _open
= _controllers
["windows-default"].open
113 # Platform support for MacOS
114 elif sys
.platform
== "darwin":
115 _controllers
["open"] = Controller("open")
116 _open
= _controllers
["open"].open
119 # Platform support for Unix
123 from commands
import getoutput
125 from subprocess
import getoutput
127 # @WARNING: use the private API of the webbrowser module
128 from webbrowser
import _iscommand
130 class KfmClient(Controller
):
131 """Controller for the KDE kfmclient program."""
133 def __init__(self
, kfmclient
="kfmclient"):
134 super(KfmClient
, self
).__init
__(kfmclient
, "exec")
135 self
.kde_version
= self
.detect_kde_version()
137 def detect_kde_version(self
):
140 info
= getoutput("kde-config --version")
142 for line
in info
.splitlines():
143 if line
.startswith("KDE"):
144 kde_version
= line
.split(":")[-1].strip()
146 except (OSError, RuntimeError):
151 def fixreturncode(self
, returncode
):
152 if returncode
is not None and self
.kde_version
> "3.5.4":
157 def detect_desktop_environment():
158 """Checks for known desktop environments
160 Return the desktop environments name, lowercase (kde, gnome, xfce)
165 desktop_environment
= "generic"
167 if os
.environ
.get("KDE_FULL_SESSION") == "true":
168 desktop_environment
= "kde"
169 elif os
.environ
.get("GNOME_DESKTOP_SESSION_ID"):
170 desktop_environment
= "gnome"
173 info
= getoutput("xprop -root _DT_SAVE_MODE")
174 if ' = "xfce4"' in info
:
175 desktop_environment
= "xfce"
176 except (OSError, RuntimeError):
179 return desktop_environment
181 def register_X_controllers():
182 if _iscommand("kfmclient"):
183 _controllers
["kde-open"] = KfmClient()
185 for command
in ("gnome-open", "exo-open", "xdg-open"):
186 if _iscommand(command
):
187 _controllers
[command
] = Controller(command
)
191 "gnome": "gnome-open",
196 desktop_environment
= detect_desktop_environment()
199 controller_name
= controllers_map
[desktop_environment
]
200 return _controllers
[controller_name
].open
203 if "xdg-open" in _controllers
:
204 return _controllers
["xdg-open"].open
206 return webbrowser
.open
208 if os
.environ
.get("DISPLAY"):
209 register_X_controllers()
214 """Open a file or a URL in the registered default application."""
216 return _open(filename
)