*** webbrowser.py.orig Thu Dec 16 18:47:38 2004 --- webbrowser.py Wed Mar 30 14:45:57 2005 *************** *** 1,7 **** --- 1,9 ---- + #! /usr/bin/env python """Interfaces for launching and remotely controlling Web browsers.""" import os import sys + import stat __all__ = ["Error", "open", "get", "register"] *************** *** 11,19 **** _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(using=None): """Return a browser launcher instance appropriate for the environment.""" --- 13,25 ---- _browsers = {} # Dictionary of available browser controllers _tryorder = [] # Preference order of available browsers ! def register(name, klass, instance=None, update_tryorder=1): """Register a browser connector and, optionally, connection.""" _browsers[name.lower()] = [klass, instance] + if update_tryorder > 0: + _tryorder.append(name) + elif update_tryorder < 0: + _tryorder.insert(0, name) def get(using=None): """Return a browser launcher instance appropriate for the environment.""" *************** *** 38,52 **** raise Error("could not locate runnable browser") # Please note: the following definition hides a builtin function. def open(url, new=0, autoraise=1): ! get().open(url, new, autoraise) ! def open_new(url): ! get().open(url, 1) ! def _synthesize(browser): """Attempt to synthesize a controller base on existing controllers. This is useful to create a controller when a user specifies a path to --- 44,69 ---- raise Error("could not locate runnable browser") # Please note: the following definition hides a builtin function. + # It is recommended one does "import webbrowser" and uses webbrowser.open(url) + # instead of "from webbrowser import *". def open(url, new=0, autoraise=1): ! for name in _tryorder: ! browser = get(name) ! if browser.open(url, new, autoraise): ! return True ! return False ! ! def open_new_win(url): ! return open(url, 1) ! open_new = open_new_win + def open_new_tab(url): + return open(url, 2) ! ! def _synthesize(browser, update_tryorder=1): """Attempt to synthesize a controller base on existing controllers. This is useful to create a controller when a user specifies a path to *************** *** 58,66 **** executable for the requested browser, return [None, None]. """ ! if not os.path.exists(browser): return [None, None] ! name = os.path.basename(browser) try: command = _browsers[name.lower()] except KeyError: --- 75,84 ---- executable for the requested browser, return [None, None]. """ ! cmd = browser.split()[0] ! if not _iscommand(cmd): return [None, None] ! name = os.path.basename(cmd) try: command = _browsers[name.lower()] except KeyError: *************** *** 72,203 **** controller = copy.copy(controller) controller.name = browser controller.basename = os.path.basename(browser) ! register(browser, None, controller) return [None, controller] return [None, None] def _iscommand(cmd): ! """Return True if cmd can be found on the executable search path.""" path = os.environ.get("PATH") if not path: ! return False for d in path.split(os.pathsep): exe = os.path.join(d, cmd) ! if os.path.isfile(exe): ! return True ! return False ! PROCESS_CREATION_DELAY = 4 - class GenericBrowser: - def __init__(self, cmd): - self.name, self.args = cmd.split(None, 1) - self.basename = os.path.basename(self.name) ! def open(self, url, new=0, autoraise=1): ! assert "'" not in url ! command = "%s %s" % (self.name, self.args) ! os.system(command % url) ! def open_new(self, url): ! self.open(url) - class Netscape: - "Launcher class for Netscape browsers." def __init__(self, name): self.name = name self.basename = os.path.basename(name) ! def _remote(self, action, autoraise): ! raise_opt = ("-noraise", "-raise")[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 &" % self.name) ! time.sleep(PROCESS_CREATION_DELAY) ! rc = os.system(cmd) return not rc def open(self, url, new=0, autoraise=1): ! if new: ! self._remote("openURL(%s, new-window)"%url, autoraise) else: ! self._remote("openURL(%s)" % url, autoraise) ! def open_new(self, url): ! self.open(url, 1) ! class Galeon: ! """Launcher class for Galeon browsers.""" ! def __init__(self, name): ! self.name = name ! self.basename = os.path.basename(name) ! def _remote(self, action, autoraise): ! raise_opt = ("--noraise", "")[autoraise] ! cmd = "%s %s %s >/dev/null 2>&1" % (self.name, raise_opt, action) ! rc = os.system(cmd) ! if rc: ! import time ! os.system("%s >/dev/null 2>&1 &" % self.name) ! time.sleep(PROCESS_CREATION_DELAY) ! rc = os.system(cmd) ! return not rc def open(self, url, new=0, autoraise=1): ! if new: ! self._remote("-w '%s'" % url, autoraise) ! else: ! self._remote("-n '%s'" % url, autoraise) ! def open_new(self, url): ! self.open(url, 1) ! 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 __init__(self): if _iscommand("konqueror"): self.name = self.basename = "konqueror" else: self.name = self.basename = "kfm" ! def _remote(self, action): cmd = "kfmclient %s >/dev/null 2>&1" % action ! rc = os.system(cmd) if rc: - import time if self.basename == "konqueror": ! os.system(self.name + " --silent &") else: ! os.system(self.name + " -d &") ! time.sleep(PROCESS_CREATION_DELAY) ! rc = os.system(cmd) return not rc def open(self, url, new=1, autoraise=1): # XXX Currently I know no way to prevent KFM from # opening a new win. assert "'" not in url ! self._remote("openURL '%s'" % url) ! open_new = open ! 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! --- 90,302 ---- controller = copy.copy(controller) controller.name = browser controller.basename = os.path.basename(browser) ! register(browser, None, controller, update_tryorder) return [None, controller] return [None, None] + if sys.platform[:3] == "win": + def _isexecutable(cmd): + cmd = cmd.lower() + if os.path.isfile(cmd) and ( + cmd.endswith(".exe") or cmd.endswith(".bat")): + return True + for ext in ".exe", ".bat": + if os.path.isfile(cmd + ext): + return True + return False + else: + def _isexecutable(cmd): + if os.path.isfile(cmd): + mode = os.stat(cmd)[stat.ST_MODE] + if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH: + return True + return False + def _iscommand(cmd): ! """Return True if cmd is executable or can be found on the executable ! search path.""" ! if _isexecutable(cmd): ! return cmd path = os.environ.get("PATH") if not path: ! return None for d in path.split(os.pathsep): exe = os.path.join(d, cmd) ! if _isexecutable(exe): ! return exe ! return None ! _DEBUG = 0 # debug level + def _run(cmd): + if _DEBUG >= 2: + print "Running command '%s'" % cmd + rc = os.system(cmd) + if _DEBUG >= 2: + print " result: %d" % rc + return rc ! # General parent classes ! class NoNewTab(object): ! """For browsers that don't support new-tab functionality""" + def open_new_tab(self, url): + return self.open_new_win(url) + + class UnixBrowser(NoNewTab): + "Parent class for all Unix browsers" + + raise_opts = None + + remote_cmd = '' + remote_action = None + remote_action_newwin = None + remote_action_newtab = None def __init__(self, name): self.name = name self.basename = os.path.basename(name) ! def _remote(self, url, action, autoraise): ! raise_opt = self.raise_opts and self.raise_opts[autoraise] or '' ! cmd = "%s %s %s '%s' >/dev/null 2>&1" % (self.name, raise_opt, ! self.remote_cmd, action) ! rc = _run(cmd) if rc: ! rc = _run("%s %s" % (self.name, url)) return not rc def open(self, url, new=0, autoraise=1): ! assert "'" not in url ! if new == 0: ! action = self.remote_action ! elif new == 1: ! action = self.remote_action_newwin ! elif new == 2: ! if self.remote_action_newtab is None: ! action = self.remote_action_newwin ! else: ! action = self.remote_action_newtab else: ! raise Error("Bad 'new' parameter to open(); expected 0, 1, or 2, got %s" % new) ! return self._remote(url, action % url, autoraise) ! def open_new_win(self, url): ! return self.open(url, 1) + open_new = open_new_win ! def open_new_tab(self, url): ! return self.open(url, 2) ! ! class GenericBrowser(NoNewTab): ! def __init__(self, cmd): ! self.name, self.args = cmd.split(None, 1) ! self.basename = os.path.basename(self.name) def open(self, url, new=0, autoraise=1): ! assert "'" not in url ! command = "%s %s" % (self.name, self.args) ! rc = _run(command % url) ! return not rc ! ! def open_new_win(self, url): ! return self.open(url) ! ! open_new = open_new_win ! ! ! class Mozilla(UnixBrowser): ! "Launcher class for Mozilla/Netscape browsers." ! ! raise_opts = ("-noraise", "-raise") ! ! remote_cmd = '-remote' ! remote_action = "openURL(%s)" ! remote_action_newwin = "openURL(%s,new-window)" ! remote_action_newtab = "openURL(%s,new-tab)" ! ! Netscape = Mozilla ! ! ! class Galeon(UnixBrowser): ! """Launcher class for Galeon/Epiphany browsers.""" ! raise_opts = ("-noraise", "") ! remote_action = "-n '%s'" ! remote_action_newwin = "-w '%s'" ! class Konqueror(NoNewTab): """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 __init__(self): if _iscommand("konqueror"): self.name = self.basename = "konqueror" else: self.name = self.basename = "kfm" ! def _remote(self, url, action): cmd = "kfmclient %s >/dev/null 2>&1" % action ! rc = _run(cmd) if rc: if self.basename == "konqueror": ! rc = _run(self.name + " --silent '%s'" % url) else: ! rc = _run(self.name + " -d '%s'" % url) return not rc def open(self, url, new=1, autoraise=1): # XXX Currently I know no way to prevent KFM from # opening a new win. assert "'" not in url ! ok = self._remote(url, "openURL '%s'" % url) ! return ok ! ! open_new_win = open_new = open ! ! ! class Opera(UnixBrowser): ! "Launcher class for Opera browser." ! raise_opts = ("", "-raise") + remote_cmd = '-remote' + remote_action = "openURL(%s)" + remote_action_newwin = "openURL(%s,new-window)" + remote_action_newtab = "openURL(%s,new-page)" ! ! class Elinks(UnixBrowser): ! "Launcher class for Elinks browsers." ! ! remote_cmd = '-remote' ! remote_action = "openURL(%s)" ! remote_action_newwin = "openURL(%s,new-window)" ! remote_action_newtab = "openURL(%s,new-tab)" ! ! def _remote(self, url, action, autoraise): ! # elinks doesn't like its stdout to be redirected - ! # it uses redirected stdout as a signal to do -dump ! cmd = "%s %s '%s' 2>/dev/null" % (self.name, ! self.remote_cmd, action) ! rc = _run(cmd) ! if rc: ! rc = _run("%s %s" % (self.name, url)) ! return not rc ! ! ! class Grail(NoNewTab): # 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! *************** *** 237,329 **** def open(self, url, new=0, autoraise=1): if new: ! self._remote("LOADNEW " + url) else: ! self._remote("LOAD " + url) ! def open_new(self, url): ! self.open(url, 1) ! ! class WindowsDefault: ! def open(self, url, new=0, autoraise=1): ! os.startfile(url) ! ! def open_new(self, url): ! self.open(url) # # Platform support for Unix # ! # 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"): ! _tryorder = ["links", "lynx", "w3m"] ! ! # Easy cases first -- register console browsers if we have them. ! if os.environ.get("TERM"): ! # The Links browser ! if _iscommand("links"): ! register("links", None, GenericBrowser("links '%s'")) ! # The Lynx browser ! if _iscommand("lynx"): ! register("lynx", None, GenericBrowser("lynx '%s'")) ! # The w3m browser ! if _iscommand("w3m"): ! register("w3m", None, GenericBrowser("w3m '%s'")) ! ! # X browsers have more in the way of options ! if os.environ.get("DISPLAY"): ! _tryorder = ["galeon", "skipstone", ! "mozilla-firefox", "mozilla-firebird", "mozilla", "netscape", ! "kfm", "grail"] + _tryorder ! ! # First, the Netscape series ! for browser in ("mozilla-firefox", "mozilla-firebird", ! "mozilla", "netscape"): ! if _iscommand(browser): ! register(browser, None, Netscape(browser)) ! ! # Next, Mosaic -- old but still in use. ! if _iscommand("mosaic"): ! register("mosaic", None, GenericBrowser( ! "mosaic '%s' >/dev/null &")) ! ! # Gnome's Galeon ! if _iscommand("galeon"): ! register("galeon", None, Galeon("galeon")) ! ! # Skipstone, another Gtk/Mozilla based browser ! if _iscommand("skipstone"): ! register("skipstone", None, GenericBrowser( ! "skipstone '%s' >/dev/null &")) ! ! # Konqueror/kfm, the KDE browser. ! if _iscommand("kfm") or _iscommand("konqueror"): ! register("kfm", Konqueror, Konqueror()) ! ! # Grail, the Python browser. ! if _iscommand("grail"): ! register("grail", Grail, None) ! ! ! class InternetConfig: ! def open(self, url, new=0, autoraise=1): ! ic.launchurl(url) ! def open_new(self, url): ! self.open(url) # # Platform support for Windows # if sys.platform[:3] == "win": ! _tryorder = ["netscape", "windows-default"] register("windows-default", WindowsDefault) # --- 336,427 ---- def open(self, url, new=0, autoraise=1): if new: ! ok = self._remote("LOADNEW " + url) else: ! ok = self._remote("LOAD " + url) ! return ok ! def open_new_win(self, url): ! return self.open(url, 1) ! open_new = open_new_win # # Platform support for Unix # ! # These are the right tests because all these Unix browsers require either ! # a console terminal or an X display to run. ! # Prefer X browsers if present ! if os.environ.get("DISPLAY"): + # First, the Mozilla/Netscape browsers + for browser in ("mozilla", "mozilla-firefox", "mozilla-firebird", + "firefox", "firebird", "netscape"): + if _iscommand(browser): + register(browser, None, Mozilla(browser)) + + # Gnome's Galeon and Epiphany + for browser in ("galeon", "epiphany"): + if _iscommand(browser): + register(browser, None, Galeon(browser)) + + # Skipstone, another Gtk/Mozilla based browser + if _iscommand("skipstone"): + register("skipstone", None, GenericBrowser("skipstone '%s'")) + + # Konqueror/kfm, the KDE browser. + if _iscommand("kfm") or _iscommand("konqueror"): + register("kfm", Konqueror, Konqueror()) + + # Next, Mosaic -- old but still in use. + if _iscommand("mosaic"): + register("mosaic", None, GenericBrowser("mosaic '%s'")) + + # Opera, quite popular + if _iscommand("opera"): + register("opera", None, Opera("opera")) + + # Grail, the Python browser. + if _iscommand("grail"): + register("grail", Grail, None) + + # Also try console browsers + if os.environ.get("TERM"): + # The Links/elinks browsers + if _iscommand("links"): + register("links", None, GenericBrowser("links '%s'")) + if _iscommand("elinks"): + register("elinks", None, Elinks("elinks")) + # The Lynx browser , + if _iscommand("lynx"): + register("lynx", None, GenericBrowser("lynx '%s'")) + # The w3m browser + if _iscommand("w3m"): + register("w3m", None, GenericBrowser("w3m '%s'")) # # Platform support for Windows # if sys.platform[:3] == "win": ! class WindowsDefault(NoNewTab): ! def open(self, url, new=0, autoraise=1): ! os.startfile(url) ! return True # Oh, my... ! ! def open_new_win(self, url): ! return self.open(url) ! ! open_new = open_new_win ! ! _tryorder = [] ! _browsers = {} ! # Prefer mozilla/netscape/opera if present ! for browser in ("mozilla", "firefox", "firebird", "netscape", "opera"): ! if _iscommand(browser): ! register(browser, None, GenericBrowser(browser + ' %s')) register("windows-default", WindowsDefault) # *************** *** 335,370 **** except ImportError: pass else: ! # internet-config is the only supported controller on MacOS, ! # so don't mess with the default! ! _tryorder = ["internet-config"] ! register("internet-config", InternetConfig) # # Platform support for OS/2 # ! if sys.platform[:3] == "os2" and _iscommand("netscape.exe"): ! _tryorder = ["os2netscape"] register("os2netscape", None, ! GenericBrowser("start netscape.exe %s")) # OK, now that we know what the default preference orders for each # platform are, allow user to override them with the BROWSER variable. - # if "BROWSER" in os.environ: ! # 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(os.pathsep) ! ! for cmd in _tryorder: ! if not cmd.lower() in _browsers: ! if _iscommand(cmd.lower()): ! register(cmd.lower(), None, GenericBrowser( ! "%s '%%s'" % cmd.lower())) ! cmd = None # to make del work if _tryorder was empty ! del cmd - _tryorder = filter(lambda x: x.lower() in _browsers - or x.find("%s") > -1, _tryorder) # what to do if _tryorder is now empty? --- 433,559 ---- except ImportError: pass else: ! class InternetConfig(NoNewTab): ! def open(self, url, new=0, autoraise=1): ! ic.launchurl(url) ! return True # Any way to get status? ! ! def open_new_win(self, url): ! return self.open(url) ! ! open_new = open_new_win ! ! register("internet-config", InternetConfig, update_tryorder=-1) ! ! if sys.platform == 'darwin': ! # Adapted from patch submitted to SourceForge by Steven J. Burr ! class MacOSX(NoNewTab): ! """Launcher class for Aqua browsers on Mac OS X ! ! Optionally specify a browser name on instantiation. Note that this ! will not work for Aqua browsers if the user has moved the application ! package after installation. ! ! If no browser is specified, the default browser, as specified in the ! Internet System Preferences panel, will be used. ! """ ! def __init__(self, name): ! self.name = name ! ! def open(self, url, new=0, autoraise=1): ! assert "'" not in url ! if self.name == "default": ! # User called open, open_new or get without a browser parameter ! script = _safequote('open location "%s"', url) # opens in default browser ! else: ! # User called get and chose a browser ! if self.name == "OmniWeb": ! toWindow = "" ! else: ! # Include toWindow parameter of OpenURL command for browsers ! # that support it. 0 == new window; -1 == existing ! toWindow = "toWindow %d" % (new - 1) ! cmd = _safequote('OpenURL "%s"', url) ! script = '''tell application "%s" ! activate ! %s %s ! end tell''' % (self.name, cmd, toWindow) ! # Open pipe to AppleScript through osascript command ! osapipe = os.popen("osascript", "w") ! if osapipe is None: ! return False ! # Write script to osascript's stdin ! osapipe.write(script) ! rc = osapipe.close() ! return not rc ! ! def open_new_win(self, url): ! return self.open(url, 1) ! ! open_new = open_new_win ! ! # Don't clear _tryorder or _browsers since OS X can use above Unix support ! # (but we prefer using the OS X specific stuff) ! register("MacOSX", None, MacOSX('default'), -1) ! # # Platform support for OS/2 # ! if sys.platform[:3] == "os2" and _iscommand("netscape"): ! _tryorder = [] ! _browsers = {} register("os2netscape", None, ! GenericBrowser("start netscape %s"), -1) ! # OK, now that we know what the default preference orders for each # platform are, allow user to override them with the BROWSER variable. if "BROWSER" in os.environ: ! _userchoices = os.environ["BROWSER"].split(os.pathsep) ! _userchoices.reverse() ! ! # Treat choices in same way as if passed into get() but do register ! # and prepend to _tryorder ! for cmdline in _userchoices: ! if cmdline != '': ! _synthesize(cmdline, -1) ! cmdline = None # to make del work if _userchoices was empty ! del cmdline ! del _userchoices # what to do if _tryorder is now empty? + + + def main(): + global _DEBUG + import getopt + usage = """Usage: %s [-n | -t] url + -n: open new window + -t: open new tab""" % sys.argv[0] + try: + opts, args = getopt.getopt(sys.argv[1:], 'ntd') + except getopt.error, msg: + print >>sys.stderr, msg + print >>sys.stderr, usage + sys.exit(1) + new_win = 0 + for o, a in opts: + if o == '-n': new_win = 1 + elif o == '-t': new_win = 2 + elif o == '-d': _DEBUG += 1 + if len(args) <> 1: + print >>sys.stderr, usage + sys.exit(1) + + if _DEBUG: + print "Try order:", _tryorder + print "Browsers:", _browsers + url = args[0] + ok = open(url, new_win) + if _DEBUG: + print "Result:", ok + + if __name__ == "__main__": + main()