This commit was manufactured by cvs2svn to create tag 'r22a4-fork'.
[python/dscho.git] / Lib / webbrowser.py
blobdac40f62c2920058a30c6b148700bffed2e4c326
1 """Interfaces for launching and remotely controlling Web browsers."""
3 import os
4 import sys
6 __all__ = ["Error", "open", "get", "register"]
8 class Error(Exception):
9 pass
11 _browsers = {} # Dictionary of available browser controllers
12 _tryorder = [] # Preference order of available browsers
14 def register(name, klass, instance=None):
15 """Register a browser connector and, optionally, connection."""
16 _browsers[name.lower()] = [klass, instance]
18 def get(using=None):
19 """Return a browser launcher instance appropriate for the environment."""
20 if using:
21 alternatives = [using]
22 else:
23 alternatives = _tryorder
24 for browser in alternatives:
25 if browser.find('%s') > -1:
26 # User gave us a command line, don't mess with it.
27 return GenericBrowser(browser)
28 else:
29 # User gave us a browser name.
30 try:
31 command = _browsers[browser.lower()]
32 except KeyError:
33 command = _synthesize(browser)
34 if command[1] is None:
35 return command[0]()
36 else:
37 return command[1]
38 raise Error("could not locate runnable browser")
40 # Please note: the following definition hides a builtin function.
42 def open(url, new=0, autoraise=1):
43 get().open(url, new, autoraise)
45 def open_new(url):
46 get().open(url, 1)
49 def _synthesize(browser):
50 """Attempt to synthesize a controller base on existing controllers.
52 This is useful to create a controller when a user specifies a path to
53 an entry in the BROWSER environment variable -- we can copy a general
54 controller to operate using a specific installation of the desired
55 browser in this way.
57 If we can't create a controller in this way, or if there is no
58 executable for the requested browser, return [None, None].
60 """
61 if not os.path.exists(browser):
62 return [None, None]
63 name = os.path.basename(browser)
64 try:
65 command = _browsers[name.lower()]
66 except KeyError:
67 return [None, None]
68 # now attempt to clone to fit the new name:
69 controller = command[1]
70 if controller and name.lower() == controller.basename:
71 import copy
72 controller = copy.copy(controller)
73 controller.name = browser
74 controller.basename = os.path.basename(browser)
75 register(browser, None, controller)
76 return [None, controller]
77 return [None, None]
80 def _iscommand(cmd):
81 """Return true if cmd can be found on the executable search path."""
82 path = os.environ.get("PATH")
83 if not path:
84 return 0
85 for d in path.split(os.pathsep):
86 exe = os.path.join(d, cmd)
87 if os.path.isfile(exe):
88 return 1
89 return 0
92 PROCESS_CREATION_DELAY = 4
95 class GenericBrowser:
96 def __init__(self, cmd):
97 self.name, self.args = cmd.split(None, 1)
98 self.basename = os.path.basename(self.name)
100 def open(self, url, new=0, autoraise=1):
101 command = "%s %s" % (self.name, self.args)
102 os.system(command % url)
104 def open_new(self, url):
105 self.open(url)
108 class Netscape:
109 "Launcher class for Netscape browsers."
110 def __init__(self, name):
111 self.name = name
112 self.basename = os.path.basename(name)
114 def _remote(self, action, autoraise):
115 raise_opt = ("-noraise", "-raise")[autoraise]
116 cmd = "%s %s -remote '%s' >/dev/null 2>&1" % (self.name,
117 raise_opt,
118 action)
119 rc = os.system(cmd)
120 if rc:
121 import time
122 os.system("%s &" % self.name)
123 time.sleep(PROCESS_CREATION_DELAY)
124 rc = os.system(cmd)
125 return not rc
127 def open(self, url, new=0, autoraise=1):
128 if new:
129 self._remote("openURL(%s, new-window)"%url, autoraise)
130 else:
131 self._remote("openURL(%s)" % url, autoraise)
133 def open_new(self, url):
134 self.open(url, 1)
137 class Konqueror:
138 """Controller for the KDE File Manager (kfm, or Konqueror).
140 See http://developer.kde.org/documentation/other/kfmclient.html
141 for more information on the Konqueror remote-control interface.
144 def __init__(self):
145 if _iscommand("konqueror"):
146 self.name = self.basename = "konqueror"
147 else:
148 self.name = self.basename = "kfm"
150 def _remote(self, action):
151 cmd = "kfmclient %s >/dev/null 2>&1" % action
152 rc = os.system(cmd)
153 if rc:
154 import time
155 if self.basename == "konqueror":
156 os.system(self.name + " --silent &")
157 else:
158 os.system(self.name + " -d &")
159 time.sleep(PROCESS_CREATION_DELAY)
160 rc = os.system(cmd)
161 return not rc
163 def open(self, url, new=1, autoraise=1):
164 # XXX Currently I know no way to prevent KFM from
165 # opening a new win.
166 self._remote("openURL %s" % url)
168 open_new = open
171 class Grail:
172 # There should be a way to maintain a connection to Grail, but the
173 # Grail remote control protocol doesn't really allow that at this
174 # point. It probably neverwill!
175 def _find_grail_rc(self):
176 import glob
177 import pwd
178 import socket
179 import tempfile
180 tempdir = os.path.join(tempfile.gettempdir(),
181 ".grail-unix")
182 user = pwd.getpwuid(_os.getuid())[0]
183 filename = os.path.join(tempdir, user + "-*")
184 maybes = glob.glob(filename)
185 if not maybes:
186 return None
187 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
188 for fn in maybes:
189 # need to PING each one until we find one that's live
190 try:
191 s.connect(fn)
192 except socket.error:
193 # no good; attempt to clean it out, but don't fail:
194 try:
195 os.unlink(fn)
196 except IOError:
197 pass
198 else:
199 return s
201 def _remote(self, action):
202 s = self._find_grail_rc()
203 if not s:
204 return 0
205 s.send(action)
206 s.close()
207 return 1
209 def open(self, url, new=0, autoraise=1):
210 if new:
211 self._remote("LOADNEW " + url)
212 else:
213 self._remote("LOAD " + url)
215 def open_new(self, url):
216 self.open(url, 1)
219 class WindowsDefault:
220 def open(self, url, new=0, autoraise=1):
221 os.startfile(url)
223 def open_new(self, url):
224 self.open(url)
227 # Platform support for Unix
230 # This is the right test because all these Unix browsers require either
231 # a console terminal of an X display to run. Note that we cannot split
232 # the TERM and DISPLAY cases, because we might be running Python from inside
233 # an xterm.
234 if os.environ.get("TERM") or os.environ.get("DISPLAY"):
235 _tryorder = ("mozilla","netscape","kfm","grail","links","lynx","w3m")
237 # Easy cases first -- register console browsers if we have them.
238 if os.environ.get("TERM"):
239 # The Links browser <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
240 if _iscommand("links"):
241 register("links", None, GenericBrowser("links %s"))
242 # The Lynx browser <http://lynx.browser.org/>
243 if _iscommand("lynx"):
244 register("lynx", None, GenericBrowser("lynx %s"))
245 # The w3m browser <http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/>
246 if _iscommand("w3m"):
247 register("w3m", None, GenericBrowser("w3m %s"))
249 # X browsers have more in the way of options
250 if os.environ.get("DISPLAY"):
251 # First, the Netscape series
252 if _iscommand("netscape") or _iscommand("mozilla"):
253 if _iscommand("mozilla"):
254 register("mozilla", None, Netscape("mozilla"))
255 if _iscommand("netscape"):
256 register("netscape", None, Netscape("netscape"))
258 # Next, Mosaic -- old but still in use.
259 if _iscommand("mosaic"):
260 register("mosaic", None, GenericBrowser("mosaic %s >/dev/null &"))
262 # Konqueror/kfm, the KDE browser.
263 if _iscommand("kfm") or _iscommand("konqueror"):
264 register("kfm", Konqueror, Konqueror())
266 # Grail, the Python browser.
267 if _iscommand("grail"):
268 register("grail", Grail, None)
271 class InternetConfig:
272 def open(self, url, new=0, autoraise=1):
273 ic.launchurl(url)
275 def open_new(self, url):
276 self.open(url)
280 # Platform support for Windows
283 if sys.platform[:3] == "win":
284 _tryorder = ("netscape", "windows-default")
285 register("windows-default", WindowsDefault)
288 # Platform support for MacOS
291 try:
292 import ic
293 except ImportError:
294 pass
295 else:
296 # internet-config is the only supported controller on MacOS,
297 # so don't mess with the default!
298 _tryorder = ("internet-config")
299 register("internet-config", InternetConfig)
301 # OK, now that we know what the default preference orders for each
302 # platform are, allow user to override them with the BROWSER variable.
304 if os.environ.has_key("BROWSER"):
305 # It's the user's responsibility to register handlers for any unknown
306 # browser referenced by this value, before calling open().
307 _tryorder = os.environ["BROWSER"].split(":")
309 for cmd in _tryorder:
310 if not _browsers.has_key(cmd.lower()):
311 if _iscommand(cmd.lower()):
312 register(cmd.lower(), None, GenericBrowser("%s %%s" % cmd.lower()))
314 _tryorder = filter(lambda x: _browsers.has_key(x.lower())
315 or x.find("%s") > -1, _tryorder)
316 # what to do if _tryorder is now empty?