diff options
author | Eric S. Raymond <esr@thyrsus.com> | 2001-01-23 13:16:32 (GMT) |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2001-01-23 13:16:32 (GMT) |
commit | f7f185116a8274b105edc1be64ffc9c8061c7f43 (patch) | |
tree | 24db0146adaff66740070bacf5478f85bc5a5eb7 /Lib/webbrowser.py | |
parent | f87d8570800699a24e79df14d9ba19f62fee0a6b (diff) | |
download | cpython-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.py | 400 |
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 |