summaryrefslogtreecommitdiffstats
path: root/Lib/webbrowser.py
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2001-01-23 13:16:32 (GMT)
committerEric S. Raymond <esr@thyrsus.com>2001-01-23 13:16:32 (GMT)
commitf7f185116a8274b105edc1be64ffc9c8061c7f43 (patch)
tree24db0146adaff66740070bacf5478f85bc5a5eb7 /Lib/webbrowser.py
parentf87d8570800699a24e79df14d9ba19f62fee0a6b (diff)
downloadcpython-f7f185116a8274b105edc1be64ffc9c8061c7f43.zip
cpython-f7f185116a8274b105edc1be64ffc9c8061c7f43.tar.gz
cpython-f7f185116a8274b105edc1be64ffc9c8061c7f43.tar.bz2
Rewrite webbrowser.py to fix various bugs following Ka-Ping Yee's
complaints. The new version moves most of its initialization to package load time; it's simpler, faster, smaller, and adds support for Mozilla and Links. Interpretation of the BROWSER variable now works and is documented. The open_new entry point and methods are marked "deprecated; may be removed in 2.1".
Diffstat (limited to 'Lib/webbrowser.py')
-rw-r--r--Lib/webbrowser.py400
1 files changed, 222 insertions, 178 deletions
diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py
index 2fa1148..36dfdc7 100644
--- a/Lib/webbrowser.py
+++ b/Lib/webbrowser.py
@@ -1,212 +1,239 @@
-"""Remote-control interfaces to some browsers."""
+"""Remote-control interfaces to common browsers."""
import os
import sys
-
-PROCESS_CREATION_DELAY = 4
-
-
class Error(Exception):
pass
-
-_browsers = {}
+_browsers = {} # Dictionary of available browser controllers
+_tryorder = [] # Preference order of available browsers
def register(name, klass, instance=None):
"""Register a browser connector and, optionally, connection."""
_browsers[name.lower()] = [klass, instance]
-
-def get(name=None):
- """Retrieve a connection to a browser by type name, or the default
- browser."""
- name = name or DEFAULT_BROWSER
- try:
- L = _browsers[name.lower()]
- except KeyError:
- raise ValueError, "unknown browser type: " + `name`
- if L[1] is None:
- L[1] = L[0]()
- return L[1]
-
+def get(using=None):
+ """Return a browser launcher instance appropriate for the environment."""
+ if using:
+ alternatives = [using]
+ else:
+ alternatives = _tryorder
+ for browser in alternatives:
+ if browser.find('%s') > -1:
+ # User gave us a command line, don't mess with it.
+ return browser
+ else:
+ # User gave us a browser name.
+ command = _browsers[browser.lower()]
+ if command[1] is None:
+ return command[0]()
+ else:
+ return command[1]
+ raise Error("could not locate runnable browser")
# Please note: the following definition hides a builtin function.
def open(url, new=0):
get().open(url, new)
+def open_new(url): # Marked deprecated. May be removed in 2.1.
+ get().open(url, 1)
-def open_new(url):
- get().open_new(url)
-
-
-def _iscommand(cmd):
- """Return true if cmd can be found on the executable search path."""
- path = os.environ.get("PATH")
- if not path:
- return 0
- for d in path.split(os.pathsep):
- exe = os.path.join(d, cmd)
- if os.path.isfile(exe):
- return 1
- return 0
-
+#
+# Everything after this point initializes _browsers and _tryorder,
+# then disappears. Some class definitions and instances remain
+# live through these globals, but only the minimum set needed to
+# support the user's platform.
+#
-class CommandLineBrowser:
- _browsers = []
- if os.environ.get("DISPLAY"):
- _browsers.extend([
- ("netscape", "netscape %s >/dev/null &"),
- ("mosaic", "mosaic %s >/dev/null &"),
- ])
- _browsers.extend([
- ("lynx", "lynx %s"),
- ("w3m", "w3m %s"),
- ])
-
- def open(self, url, new=0):
- for exe, cmd in self._browsers:
- if _iscommand(exe):
- os.system(cmd % url)
- return
- raise Error("could not locate runnable browser")
-
- def open_new(self, url):
- self.open(url)
-
-register("command-line", CommandLineBrowser)
-
-
-class Netscape:
- autoRaise = 1
-
- def _remote(self, action):
- raise_opt = ("-noraise", "-raise")[self.autoRaise]
- cmd = "netscape %s -remote '%s' >/dev/null 2>&1" % (raise_opt, action)
- rc = os.system(cmd)
- if rc:
- import time
- os.system("netscape -no-about-splash &")
- time.sleep(PROCESS_CREATION_DELAY)
- rc = os.system(cmd)
- return not rc
-
- def open(self, url, new=0):
- if new:
- self.open_new(url)
- else:
- self._remote("openURL(%s)" % url)
-
- def open_new(self, url):
- self._remote("openURL(%s, new-window)" % url)
-
-register("netscape", Netscape)
-
-
-class Konqueror:
- """Controller for the KDE File Manager (kfm, or Konqueror).
-
- See http://developer.kde.org/documentation/other/kfmclient.html
- for more information on the Konqueror remote-control interface.
-
- """
- def _remote(self, action):
- cmd = "kfmclient %s >/dev/null 2>&1" % action
- rc = os.system(cmd)
- if rc:
- import time
- os.system("kfm -d &")
- time.sleep(PROCESS_CREATION_DELAY)
- rc = os.system(cmd)
- return not rc
-
- def open(self, url, new=1):
- # XXX currently I know no way to prevent KFM from opening a new win.
- self.open_new(url)
-
- def open_new(self, url):
- self._remote("openURL %s" % url)
-
-register("kfm", Konqueror)
-
-
-class Grail:
- # There should be a way to maintain a connection to Grail, but the
- # Grail remote control protocol doesn't really allow that at this
- # point. It probably never will!
-
- def _find_grail_rc(self):
- import glob
- import pwd
- import socket
- import tempfile
- tempdir = os.path.join(tempfile.gettempdir(), ".grail-unix")
- user = pwd.getpwuid(_os.getuid())[0]
- filename = os.path.join(tempdir, user + "-*")
- maybes = glob.glob(filename)
- if not maybes:
- return None
- s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- for fn in maybes:
- # need to PING each one until we find one that's live
- try:
- s.connect(fn)
- except socket.error:
- # no good; attempt to clean it out, but don't fail:
- try:
- os.unlink(fn)
- except IOError:
- pass
- else:
- return s
+#
+# Platform support for Unix
+#
- def _remote(self, action):
- s = self._find_grail_rc()
- if not s:
+# This is the right test because all these Unix browsers require either
+# a console terminal of an X display to run. Note that we cannot split
+# the TERM and DISPLAY cases, because we might be running Python from inside
+# an xterm.
+if os.environ.get("TERM") or os.environ.get("DISPLAY"):
+ PROCESS_CREATION_DELAY = 4
+ global tryorder
+ _tryorder = ("mozilla","netscape","kfm","grail","links","lynx","w3m")
+
+ def _iscommand(cmd):
+ """Return true if cmd can be found on the executable search path."""
+ path = os.environ.get("PATH")
+ if not path:
return 0
- s.send(action)
- s.close()
- return 1
+ for d in path.split(os.pathsep):
+ exe = os.path.join(d, cmd)
+ if os.path.isfile(exe):
+ return 1
+ return 0
- def open(self, url, new=0):
- if new:
- self.open_new(url)
- else:
- self._remote("LOAD " + url)
+ class GenericBrowser:
+ def __init__(self, cmd):
+ self.command = cmd
- def open_new(self, url):
- self._remote("LOADNEW " + url)
+ def open(self, url, new=0):
+ os.system(self.command % url)
-register("grail", Grail)
+ def open_new(self, url): # Deprecated. May be removed in 2.1.
+ self.open(url)
+ # Easy cases first -- register console browsers if we have them.
+ if os.environ.get("TERM"):
+ # The Links browser <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
+ if _iscommand("links"):
+ register("links", None, GenericBrowser("links %s"))
+ # The Lynx browser <http://lynx.browser.org/>
+ if _iscommand("lynx"):
+ register("lynx", None, GenericBrowser("lynx %s"))
+ # The w3m browser <http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/>
+ if _iscommand("w3m"):
+ register("w3m", None, GenericBrowser("w3m %s"))
+
+ # X browsers have mre in the way of options
+ if os.environ.get("DISPLAY"):
+ # First, the Netscape series
+ if _iscommand("netscape") or _iscommand("mozilla"):
+ class Netscape:
+ "Launcher class for Netscape browsers."
+ autoRaise = 1
+
+ def __init__(self, name):
+ self.name = name
+
+ def _remote(self, action):
+ raise_opt = ("-noraise", "-raise")[self.autoRaise]
+ cmd = "%s %s -remote '%s' >/dev/null 2>&1" % (self.name, raise_opt, action)
+ rc = os.system(cmd)
+ if rc:
+ import time
+ os.system("%s -no-about-splash &" % self.name)
+ time.sleep(PROCESS_CREATION_DELAY)
+ rc = os.system(cmd)
+ return not rc
+
+ def open(self, url, new=0):
+ if new:
+ self._remote("openURL(%s, new-window)" % url)
+ else:
+ self._remote("openURL(%s)" % url)
+
+ # Deprecated. May be removed in 2.1.
+ def open_new(self, url):
+ self.open(url, 1)
+
+ if _iscommand("mozilla"):
+ register("mozilla", None, Netscape("mozilla"))
+ if _iscommand("netscape"):
+ register("netscape", None, Netscape("netscape"))
+
+ # Next, Mosaic -- old but still in use.
+ if _iscommand("mosaic"):
+ register("mosaic", None, GenericBrowser("mosaic %s >/dev/null &"))
+
+ # Konqueror/kfm, the KDE browser.
+ if _iscommand("kfm"):
+ class Konqueror:
+ """Controller for the KDE File Manager (kfm, or Konqueror).
+
+ See http://developer.kde.org/documentation/other/kfmclient.html
+ for more information on the Konqueror remote-control interface.
+
+ """
+ def _remote(self, action):
+ cmd = "kfmclient %s >/dev/null 2>&1" % action
+ rc = os.system(cmd)
+ if rc:
+ import time
+ os.system("kfm -d &")
+ time.sleep(PROCESS_CREATION_DELAY)
+ rc = os.system(cmd)
+ return not rc
+
+ def open(self, url, new=1):
+ # XXX Currently I know no way to prevent KFM from opening a new win.
+ self._remote("openURL %s" % url)
+
+ # Deprecated. May be removed in 2.1.
+ open_new = open
+
+
+ register("kfm", Konqueror, None)
+
+ # Grail, the Python browser.
+ if _iscommand("grail"):
+ class Grail:
+ # There should be a way to maintain a connection to
+ # Grail, but the Grail remote control protocol doesn't
+ # really allow that at this point. It probably neverwill!
+ def _find_grail_rc(self):
+ import glob
+ import pwd
+ import socket
+ import tempfile
+ tempdir = os.path.join(tempfile.gettempdir(), ".grail-unix")
+ user = pwd.getpwuid(_os.getuid())[0]
+ filename = os.path.join(tempdir, user + "-*")
+ maybes = glob.glob(filename)
+ if not maybes:
+ return None
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ for fn in maybes:
+ # need to PING each one until we find one that's live
+ try:
+ s.connect(fn)
+ except socket.error:
+ # no good; attempt to clean it out, but don't fail:
+ try:
+ os.unlink(fn)
+ except IOError:
+ pass
+ else:
+ return s
+
+ def _remote(self, action):
+ s = self._find_grail_rc()
+ if not s:
+ return 0
+ s.send(action)
+ s.close()
+ return 1
+
+ def open(self, url, new=0):
+ if new:
+ self._remote("LOADNEW " + url)
+ else:
+ self._remote("LOAD " + url)
+
+ # Deprecated. May be removed in 2.1.
+ def open_new(self, url):
+ self.open(url, 1)
+
+ register("grail", Grail, None)
-class WindowsDefault:
- def open(self, url, new=0):
- os.startfile(url)
+#
+# Platform support for Windows
+#
- def open_new(self, url):
- self.open(url)
+if sys.platform[:3] == "win":
+ global _tryorder
+ _tryorder = ("netscape", "windows-default")
+ class WindowsDefault:
+ def open(self, url, new=0):
+ os.startfile(url)
-DEFAULT_BROWSER = "command-line"
+ def open_new(self, url): # Deprecated. May be removed in 2.1.
+ self.open(url)
-if sys.platform[:3] == "win":
- del _browsers["kfm"]
register("windows-default", WindowsDefault)
- DEFAULT_BROWSER = "windows-default"
-elif os.environ.get("DISPLAY"):
- if _iscommand("netscape"):
- DEFAULT_BROWSER = "netscape"
-# If the $BROWSER environment variable is set and true, let that be
-# the name of the browser to use:
#
-DEFAULT_BROWSER = os.environ.get("BROWSER") or DEFAULT_BROWSER
-
-
-# Now try to support the MacOS world. This is the only supported
-# controller on that platform, so don't mess with the default!
+# Platform support for MacOS
+#
try:
import ic
@@ -217,9 +244,26 @@ else:
def open(self, url, new=0):
ic.launchurl(url)
- def open_new(self, url):
+ def open_new(self, url): # Deprecated. May be removed in 2.1.
self.open(url)
- _browsers.clear()
+ # internet-config is the only supported controller on MacOS,
+ # so don't mess with the default!
+ _tryorder = ("internet-config")
register("internet-config", InternetConfig)
- DEFAULT_BROWSER = "internet-config"
+
+# OK, now that we know what the default preference orders for each
+# platform are, allow user to override them with the BROWSER variable.
+#
+if os.environ.has_key("BROWSER"):
+ # It's the user's responsibility to register handlers for any unknown
+ # browser referenced by this value, before calling open().
+ _tryorder = os.environ["BROWSER"].split(":")
+else:
+ # Optimization: filter out alternatives that aren't available, so we can
+ # avoid has_key() tests at runtime. (This may also allow some unused
+ # classes and class-instance storage to be garbage-collected.)
+ _tryorder = filter(lambda x: _browsers.has_key(x.lower()) or x.find("%s")>-1,\
+ _tryorder)
+
+# end