[Python-checkins] CVS: python/dist/src/Lib webbrowser.py,1.7,1.8

Eric S. Raymond esr@users.sourceforge.net
Tue, 23 Jan 2001 05:16:35 -0800


Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv3470/Lib

Modified Files:
	webbrowser.py 
Log Message:
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".


Index: webbrowser.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/webbrowser.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -C2 -r1.7 -r1.8
*** webbrowser.py	2001/01/15 03:34:38	1.7
--- webbrowser.py	2001/01/23 13:16:32	1.8
***************
*** 1,34 ****
! """Remote-control interfaces to some browsers."""
  
  import os
  import sys
  
- 
- PROCESS_CREATION_DELAY = 4
- 
- 
  class Error(Exception):
      pass
  
  
- _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]
! 
  
  # Please note: the following definition hides a builtin function.
--- 1,35 ----
! """Remote-control interfaces to common browsers."""
  
  import os
  import sys
  
  class Error(Exception):
      pass
  
+ _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."""
!     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.
***************
*** 37,211 ****
      get().open(url, new)
  
  
! 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
- 
  
! 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
  
!     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.open_new(url)
!         else:
!             self._remote("LOAD " + url)
  
!     def open_new(self, url):
!         self._remote("LOADNEW " + url)
  
! register("grail", Grail)
  
  
! class WindowsDefault:
!     def open(self, url, new=0):
!         os.startfile(url)
  
!     def open_new(self, url):
!         self.open(url)
  
  
! DEFAULT_BROWSER = "command-line"
  
- 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!
  
  try:
--- 38,238 ----
      get().open(url, new)
  
+ def open_new(url):	# Marked deprecated.  May be removed in 2.1.
+     get().open(url, 1)
  
! #
! # 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.
! #
  
+ # 
+ # 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"):
!     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
!         for d in path.split(os.pathsep):
!             exe = os.path.join(d, cmd)
!             if os.path.isfile(exe):
!                 return 1
          return 0
  
!     class GenericBrowser:
!         def __init__(self, cmd):
!             self.command = cmd
  
!         def open(self, url, new=0):
!             os.system(self.command % url)
  
!         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)
  
! #
! # Platform support for Windows
! #
  
! if sys.platform[:3] == "win":
!     global _tryorder
!     _tryorder = ("netscape", "windows-default")
  
+     class WindowsDefault:
+         def open(self, url, new=0):
+             os.startfile(url)
  
!         def open_new(self, url):        # Deprecated.  May be removed in 2.1.
!             self.open(url)
  
      register("windows-default", WindowsDefault)
  
  #
! # Platform support for MacOS
! #
  
  try:
***************
*** 218,225 ****
              ic.launchurl(url)
  
!         def open_new(self, url):
              self.open(url)
  
!     _browsers.clear()
      register("internet-config", InternetConfig)
!     DEFAULT_BROWSER = "internet-config"
--- 245,269 ----
              ic.launchurl(url)
  
!         def open_new(self, url):        # Deprecated.  May be removed in 2.1.
              self.open(url)
  
!     # internet-config is the only supported controller on MacOS,
!     # so don't mess with the default!
!     _tryorder = ("internet-config")
      register("internet-config", InternetConfig)
! 
! # 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