From e8f244305ef4f257f6999b69601f4316b31faa5e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 3 Oct 2005 14:16:44 +0000 Subject: [PATCH] Patch #754022: Greatly enhanced webbrowser.py. --- Doc/lib/libwebbrowser.tex | 75 ++++-- Lib/webbrowser.py | 493 +++++++++++++++++++++++++------------- Misc/NEWS | 2 + 3 files changed, 383 insertions(+), 187 deletions(-) diff --git a/Doc/lib/libwebbrowser.tex b/Doc/lib/libwebbrowser.tex index 285fcf5582a..42c76912b03 100644 --- a/Doc/lib/libwebbrowser.tex +++ b/Doc/lib/libwebbrowser.tex @@ -6,9 +6,8 @@ \moduleauthor{Fred L. Drake, Jr.}{fdrake@acm.org} \sectionauthor{Fred L. Drake, Jr.}{fdrake@acm.org} -The \module{webbrowser} module provides a very high-level interface to -allow displaying Web-based documents to users. The controller objects -are easy to use and are platform-independent. Under most +The \module{webbrowser} module provides a high-level interface to +allow displaying Web-based documents to users. Under most circumstances, simply calling the \function{open()} function from this module will do the right thing. @@ -17,19 +16,26 @@ browsers will be used if graphical browsers are not available or an X11 display isn't available. If text-mode browsers are used, the calling process will block until the user exits the browser. -Under \UNIX, if the environment variable \envvar{BROWSER} exists, it +If the environment variable \envvar{BROWSER} exists, it is interpreted to override the platform default list of browsers, as a -colon-separated list of browsers to try in order. When the value of +os.pathsep-separated list of browsers to try in order. When the value of a list part contains the string \code{\%s}, then it is interpreted as a literal browser command line to be used with the argument URL substituted for the \code{\%s}; if the part does not contain \code{\%s}, it is simply interpreted as the name of the browser to launch. -For non-\UNIX{} platforms, or when X11 browsers are available on +For non-\UNIX{} platforms, or when a remote browser is available on \UNIX, the controlling process will not wait for the user to finish -with the browser, but allow the browser to maintain its own window on -the display. +with the browser, but allow the remote browser to maintain its own +windows on the display. If remote browsers are not available on \UNIX, +the controlling process will launch a new browser and wait. + +The script \program{webbrowser} can be used as a command-line interface +for the module. It accepts an URL as the argument. It accepts the following +optional parameters: \programopt{-n} opens the URL in a new browser window, +if possible; \programopt{-t} opens the URL in a new browser page ("tab"). The +options are, naturally, mutually exclusive. The following exception is defined: @@ -40,15 +46,24 @@ The following exception is defined: The following functions are defined: \begin{funcdesc}{open}{url\optional{, new=0}\optional{, autoraise=1}} - Display \var{url} using the default browser. If \var{new} is true, - a new browser window is opened if possible. If \var{autoraise} is + Display \var{url} using the default browser. If \var{new} is 0, the + \var{url} is opened in the same browser window. If \var{new} is 1, + a new browser window is opened if possible. If \var{new} is 2, + a new browser page ("tab") is opened if possible. If \var{autoraise} is true, the window is raised if possible (note that under many window managers this will occur regardless of the setting of this variable). + \end{funcdesc} -\begin{funcdesc}{open_new}{url} +\begin{funcdesc}{open_new_win}{url} Open \var{url} in a new window of the default browser, if possible, - otherwise, open \var{url} in the only browser window. + otherwise, open \var{url} in the only browser window. Alias + \function{open_new}. +\end{funcdesc} + +\begin{funcdesc}{open_new_tab}{url} + Open \var{url} in a new page ("tab") of the default browser, if possible, + otherwise equivalent to \function{open_new_win}. \end{funcdesc} \begin{funcdesc}{get}{\optional{name}} @@ -67,7 +82,7 @@ The following functions are defined: This entry point is only useful if you plan to either set the \envvar{BROWSER} variable or call \function{get} with a nonempty - argument matching the name of a handler you declare. + argument matching the name of a handler you declare. \end{funcdesc} A number of browser types are predefined. This table gives the type @@ -76,16 +91,24 @@ corresponding instantiations for the controller classes, all defined in this module. \begin{tableiii}{l|l|c}{code}{Type Name}{Class Name}{Notes} - \lineiii{'mozilla'}{\class{Netscape('mozilla')}}{} - \lineiii{'netscape'}{\class{Netscape('netscape')}}{} - \lineiii{'mosaic'}{\class{GenericBrowser('mosaic \%s \&')}}{} + \lineiii{'mozilla'}{\class{Mozilla('mozilla')}}{} + \lineiii{'firefox'}{\class{Mozilla('mozilla')}}{} + \lineiii{'netscape'}{\class{Mozilla('netscape')}}{} + \lineiii{'galeon'}{\class{Galeon('galeon')}}{} + \lineiii{'epiphany'}{\class{Galeon('epiphany')}}{} + \lineiii{'skipstone'}{\class{GenericBrowser('skipstone \%s \&')}}{} + \lineiii{'konqueror'}{\class{Konqueror()}}{(1)} \lineiii{'kfm'}{\class{Konqueror()}}{(1)} + \lineiii{'mosaic'}{\class{GenericBrowser('mosaic \%s \&')}}{} + \lineiii{'opera'}{\class{Opera()}}{} \lineiii{'grail'}{\class{Grail()}}{} \lineiii{'links'}{\class{GenericBrowser('links \%s')}}{} + \lineiii{'elinks'}{\class{Elinks('elinks')}}{} \lineiii{'lynx'}{\class{GenericBrowser('lynx \%s')}}{} \lineiii{'w3m'}{\class{GenericBrowser('w3m \%s')}}{} \lineiii{'windows-default'}{\class{WindowsDefault}}{(2)} \lineiii{'internet-config'}{\class{InternetConfig}}{(3)} + \lineiii{'macosx'}{\class{MacOSX('default')}}{(4)} \end{tableiii} \noindent @@ -101,13 +124,15 @@ using the \program{konqueror} command with KDE 2 --- the implementation selects the best strategy for running Konqueror. \item[(2)] -Only on Windows platforms; requires the common -extension modules \module{win32api} and \module{win32con}. +Only on Windows platforms. \item[(3)] Only on MacOS platforms; requires the standard MacPython \module{ic} module, described in the \citetitle[../mac/module-ic.html]{Macintosh Library Modules} manual. + +\item[(4)] +Only on MacOS X platform. \end{description} @@ -117,12 +142,18 @@ Browser controllers provide two methods which parallel two of the module-level convenience functions: \begin{funcdesc}{open}{url\optional{, new}} - Display \var{url} using the browser handled by this controller. If - \var{new} is true, a new browser window is opened if possible. + Display \var{url} using the browser handled by this controller. + If \var{new} is 1, a new browser window is opened if possible. + If \var{new} is 2, a new browser page ("tab") is opened if possible. \end{funcdesc} -\begin{funcdesc}{open_new}{url} +\begin{funcdesc}{open_new_win}{url} Open \var{url} in a new window of the browser handled by this controller, if possible, otherwise, open \var{url} in the only - browser window. + browser window. Alias \function{open_new}. +\end{funcdesc} + +\begin{funcdesc}{open_new_tab}{url} + Open \var{url} in a new page ("tab") of the browser handled by this + controller, if possible, otherwise equivalent to \function{open_new_win}. \end{funcdesc} diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index 4750fe25cd2..6c34f8b81ac 100644 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -1,9 +1,11 @@ +#! /usr/bin/env python """Interfaces for launching and remotely controlling Web browsers.""" import os import sys +import stat -__all__ = ["Error", "open", "get", "register"] +__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] class Error(Exception): pass @@ -11,9 +13,13 @@ class Error(Exception): _browsers = {} # Dictionary of available browser controllers _tryorder = [] # Preference order of available browsers -def register(name, klass, instance=None): +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.""" @@ -26,27 +32,36 @@ def get(using=None): # User gave us a command line, don't mess with it. return GenericBrowser(browser) else: - # User gave us a browser name. + # User gave us a browser name or path. try: command = _browsers[browser.lower()] except KeyError: command = _synthesize(browser) - if command[1] is None: - return command[0]() - else: + if command[1] is not None: return command[1] + elif command[0] is not None: + return command[0]() 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): - get().open(url, new, autoraise) + for name in _tryorder: + browser = get(name) + if browser.open(url, new, autoraise): + return True + return False def open_new(url): - get().open(url, 1) + return open(url, 1) + +def open_new_tab(url): + return open(url, 2) -def _synthesize(browser): +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,9 +73,10 @@ def _synthesize(browser): executable for the requested browser, return [None, None]. """ - if not os.path.exists(browser): + cmd = browser.split()[0] + if not _iscommand(cmd): return [None, None] - name = os.path.basename(browser) + name = os.path.basename(cmd) try: command = _browsers[name.lower()] except KeyError: @@ -72,132 +88,199 @@ def _synthesize(browser): controller = copy.copy(controller) controller.name = browser controller.basename = os.path.basename(browser) - register(browser, None, controller) + 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 can be found on the executable search path.""" + """Return True if cmd is executable or can be found on the executable + search path.""" + if _isexecutable(cmd): + return True 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): + if _isexecutable(exe): return True return False -PROCESS_CREATION_DELAY = 4 +# General parent classes + +class BaseBrowser(object): + """Parent class for all browsers.""" + + def __init__(self, name=""): + self.name = name + + def open_new(self, url): + return self.open(url, 1) + + def open_new_tab(self, url): + return self.open(url, 2) -class GenericBrowser: +class GenericBrowser(BaseBrowser): + """Class for all browsers started with a command + and without remote functionality.""" + 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) + rc = os.system(command % url) + return not rc -class Netscape: - "Launcher class for Netscape browsers." - def __init__(self, name): - self.name = name - self.basename = os.path.basename(name) +class UnixBrowser(BaseBrowser): + """Parent class for all Unix browsers with remote functionality.""" - def _remote(self, action, autoraise): - raise_opt = ("-noraise", "-raise")[autoraise] - cmd = "%s %s -remote '%s' >/dev/null 2>&1" % (self.name, - raise_opt, - action) + raise_opts = None + + remote_cmd = '' + remote_action = None + remote_action_newwin = None + remote_action_newtab = None + remote_background = False + + def _remote(self, url, action, autoraise): + autoraise = int(bool(autoraise)) # always 0/1 + 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) + if remote_background: + cmd += ' &' rc = os.system(cmd) if rc: - import time - os.system("%s &" % self.name) - time.sleep(PROCESS_CREATION_DELAY) - rc = os.system(cmd) + # bad return status, try again with simpler command + rc = os.system("%s %s" % (self.name, url)) return not rc def open(self, url, new=0, autoraise=1): - if new: - self._remote("openURL(%s, new-window)"%url, autoraise) + 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: - self._remote("openURL(%s)" % url, autoraise) - - def open_new(self, url): - self.open(url, 1) + raise Error("Bad 'new' parameter to open(); expected 0, 1, or 2, got %s" % new) + return self._remote(url, action % url, autoraise) -class Galeon: - """Launcher class for Galeon browsers.""" - def __init__(self, name): - self.name = name - self.basename = os.path.basename(name) +class Mozilla(UnixBrowser): + """Launcher class for Mozilla/Netscape browsers.""" - 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 + raise_opts = ("-noraise", "-raise") - def open(self, url, new=0, autoraise=1): - if new: - self._remote("-w '%s'" % url, autoraise) - else: - self._remote("-n '%s'" % url, autoraise) + remote_cmd = '-remote' + remote_action = "openURL(%s)" + remote_action_newwin = "openURL(%s,new-window)" + remote_action_newtab = "openURL(%s,new-tab)" - def open_new(self, url): - self.open(url, 1) +Netscape = Mozilla -class Konqueror: +class Galeon(UnixBrowser): + """Launcher class for Galeon/Epiphany browsers.""" + + raise_opts = ("-noraise", "") + remote_action = "-n '%s'" + remote_action_newwin = "-w '%s'" + + remote_background = True + + +class Konqueror(BaseBrowser): """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): + def _remote(self, url, action): + # kfmclient is the new KDE way of opening URLs. cmd = "kfmclient %s >/dev/null 2>&1" % action rc = os.system(cmd) + # Fall back to other variants. 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) + if _iscommand("konqueror"): + rc = os.system(self.name + " --silent '%s' &" % url) + elif _iscommand("kfm"): + rc = os.system(self.name + " -d '%s'" % url) return not rc - def open(self, url, new=1, autoraise=1): + def open(self, url, new=0, 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 + if new == 2: + action = "newTab '%s'" % url + else: + action = "openURL '%s'" % url + ok = self._remote(url, action) + return ok -class Grail: +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 = os.system(cmd) + if rc: + rc = os.system("%s %s" % (self.name, url)) + return not rc + + +class Grail(BaseBrowser): # 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,93 +320,97 @@ class Grail: def open(self, url, new=0, autoraise=1): if new: - self._remote("LOADNEW " + url) + ok = self._remote("LOADNEW " + url) else: - self._remote("LOAD " + url) + ok = self._remote("LOAD " + url) + return ok - 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"] +# These are the right tests because all these Unix browsers require either +# a console terminal or an X display to run. - # 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'")) +# Prefer X browsers if present +if os.environ.get("DISPLAY"): - # 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 Mozilla/Netscape browsers + for browser in ("mozilla-firefox", "firefox", + "mozilla-firebird", "firebird", + "mozilla", "netscape"): + if _iscommand(browser): + register(browser, None, Mozilla(browser)) - # First, the Netscape series - for browser in ("mozilla-firefox", "mozilla-firebird", - "mozilla", "netscape"): - if _iscommand(browser): - register(browser, None, Netscape(browser)) + # The default Gnome browser + if _iscommand("gconftool-2"): + # get the web browser string from gconftool + gc = 'gconftool-2 -g /desktop/gnome/url-handlers/http/command' + out = os.popen(gc) + commd = out.read().strip() + retncode = out.close() - # Next, Mosaic -- old but still in use. - if _iscommand("mosaic"): - register("mosaic", None, GenericBrowser( - "mosaic '%s' >/dev/null &")) + # if successful, register it + if retncode == None and len(commd) != 0: + register("gnome", None, GenericBrowser( + commd + " '%s' >/dev/null &")) - # Gnome's Galeon - if _iscommand("galeon"): - register("galeon", None, Galeon("galeon")) + # Konqueror/kfm, the KDE browser. + if _iscommand("kfm") or _iscommand("konqueror"): + register("kfm", Konqueror, Konqueror()) - # Skipstone, another Gtk/Mozilla based browser - if _iscommand("skipstone"): - register("skipstone", None, GenericBrowser( - "skipstone '%s' >/dev/null &")) + # Gnome's Galeon and Epiphany + for browser in ("galeon", "epiphany"): + if _iscommand(browser): + register(browser, None, Galeon(browser)) - # Konqueror/kfm, the KDE browser. - if _iscommand("kfm") or _iscommand("konqueror"): - register("kfm", Konqueror, Konqueror()) + # Skipstone, another Gtk/Mozilla based browser + if _iscommand("skipstone"): + register("skipstone", None, GenericBrowser("skipstone '%s' &")) - # Grail, the Python browser. - if _iscommand("grail"): - register("grail", Grail, None) + # Opera, quite popular + if _iscommand("opera"): + register("opera", None, Opera("opera")) + # Next, Mosaic -- old but still in use. + if _iscommand("mosaic"): + register("mosaic", None, GenericBrowser("mosaic '%s' &")) -class InternetConfig: - def open(self, url, new=0, autoraise=1): - ic.launchurl(url) - - def open_new(self, url): - self.open(url) + # Grail, the Python browser. Does anybody still use it? + 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": - _tryorder = ["netscape", "windows-default"] + class WindowsDefault(BaseBrowser): + def open(self, url, new=0, autoraise=1): + os.startfile(url) + return True # Oh, my... + + _tryorder = [] + _browsers = {} + # Prefer mozilla/netscape/opera if present + for browser in ("firefox", "firebird", "mozilla", "netscape", "opera"): + if _iscommand(browser): + register(browser, None, GenericBrowser(browser + ' %s')) register("windows-default", WindowsDefault) # @@ -335,36 +422,112 @@ try: 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) + class InternetConfig(BaseBrowser): + def open(self, url, new=0, autoraise=1): + ic.launchurl(url) + return True # Any way to get status? + + register("internet-config", InternetConfig, update_tryorder=-1) + +if sys.platform == 'darwin': + # Adapted from patch submitted to SourceForge by Steven J. Burr + class MacOSX(BaseBrowser): + """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 + # new must be 0 or 1 + new = int(bool(new)) + 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 + + # 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.exe"): - _tryorder = ["os2netscape"] +if sys.platform[:3] == "os2" and _iscommand("netscape"): + _tryorder = [] + _browsers = {} register("os2netscape", None, - GenericBrowser("start netscape.exe %s")) + 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: - # 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) + _userchoices = os.environ["BROWSER"].split(os.pathsep) + _userchoices.reverse() -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 + # 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 -_tryorder = filter(lambda x: x.lower() in _browsers - or x.find("%s") > -1, _tryorder) # what to do if _tryorder is now empty? + + +def main(): + 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 + if len(args) <> 1: + print >>sys.stderr, usage + sys.exit(1) + + url = args[0] + open(url, new_win) + +if __name__ == "__main__": + main() diff --git a/Misc/NEWS b/Misc/NEWS index c78fa69eca7..11dd40cde36 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -254,6 +254,8 @@ Extension Modules Library ------- +- Patch #754022: Greatly enhanced webbrowser.py (by Oleg Broytmann). + - Bug #729103: pydoc.py: Fix docother() method to accept additional "parent" argument.