(root)/
Python-3.12.0/
Lib/
webbrowser.py
       1  #! /usr/bin/env python3
       2  """Interfaces for launching and remotely controlling web browsers."""
       3  # Maintained by Georg Brandl.
       4  
       5  import os
       6  import shlex
       7  import shutil
       8  import sys
       9  import subprocess
      10  import threading
      11  import warnings
      12  
      13  __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
      14  
      15  class ESC[4;38;5;81mError(ESC[4;38;5;149mException):
      16      pass
      17  
      18  _lock = threading.RLock()
      19  _browsers = {}                  # Dictionary of available browser controllers
      20  _tryorder = None                # Preference order of available browsers
      21  _os_preferred_browser = None    # The preferred browser
      22  
      23  def register(name, klass, instance=None, *, preferred=False):
      24      """Register a browser connector."""
      25      with _lock:
      26          if _tryorder is None:
      27              register_standard_browsers()
      28          _browsers[name.lower()] = [klass, instance]
      29  
      30          # Preferred browsers go to the front of the list.
      31          # Need to match to the default browser returned by xdg-settings, which
      32          # may be of the form e.g. "firefox.desktop".
      33          if preferred or (_os_preferred_browser and name in _os_preferred_browser):
      34              _tryorder.insert(0, name)
      35          else:
      36              _tryorder.append(name)
      37  
      38  def get(using=None):
      39      """Return a browser launcher instance appropriate for the environment."""
      40      if _tryorder is None:
      41          with _lock:
      42              if _tryorder is None:
      43                  register_standard_browsers()
      44      if using is not None:
      45          alternatives = [using]
      46      else:
      47          alternatives = _tryorder
      48      for browser in alternatives:
      49          if '%s' in browser:
      50              # User gave us a command line, split it into name and args
      51              browser = shlex.split(browser)
      52              if browser[-1] == '&':
      53                  return BackgroundBrowser(browser[:-1])
      54              else:
      55                  return GenericBrowser(browser)
      56          else:
      57              # User gave us a browser name or path.
      58              try:
      59                  command = _browsers[browser.lower()]
      60              except KeyError:
      61                  command = _synthesize(browser)
      62              if command[1] is not None:
      63                  return command[1]
      64              elif command[0] is not None:
      65                  return command[0]()
      66      raise Error("could not locate runnable browser")
      67  
      68  # Please note: the following definition hides a builtin function.
      69  # It is recommended one does "import webbrowser" and uses webbrowser.open(url)
      70  # instead of "from webbrowser import *".
      71  
      72  def open(url, new=0, autoraise=True):
      73      """Display url using the default browser.
      74  
      75      If possible, open url in a location determined by new.
      76      - 0: the same browser window (the default).
      77      - 1: a new browser window.
      78      - 2: a new browser page ("tab").
      79      If possible, autoraise raises the window (the default) or not.
      80      """
      81      if _tryorder is None:
      82          with _lock:
      83              if _tryorder is None:
      84                  register_standard_browsers()
      85      for name in _tryorder:
      86          browser = get(name)
      87          if browser.open(url, new, autoraise):
      88              return True
      89      return False
      90  
      91  def open_new(url):
      92      """Open url in a new window of the default browser.
      93  
      94      If not possible, then open url in the only browser window.
      95      """
      96      return open(url, 1)
      97  
      98  def open_new_tab(url):
      99      """Open url in a new page ("tab") of the default browser.
     100  
     101      If not possible, then the behavior becomes equivalent to open_new().
     102      """
     103      return open(url, 2)
     104  
     105  
     106  def _synthesize(browser, *, preferred=False):
     107      """Attempt to synthesize a controller based on existing controllers.
     108  
     109      This is useful to create a controller when a user specifies a path to
     110      an entry in the BROWSER environment variable -- we can copy a general
     111      controller to operate using a specific installation of the desired
     112      browser in this way.
     113  
     114      If we can't create a controller in this way, or if there is no
     115      executable for the requested browser, return [None, None].
     116  
     117      """
     118      cmd = browser.split()[0]
     119      if not shutil.which(cmd):
     120          return [None, None]
     121      name = os.path.basename(cmd)
     122      try:
     123          command = _browsers[name.lower()]
     124      except KeyError:
     125          return [None, None]
     126      # now attempt to clone to fit the new name:
     127      controller = command[1]
     128      if controller and name.lower() == controller.basename:
     129          import copy
     130          controller = copy.copy(controller)
     131          controller.name = browser
     132          controller.basename = os.path.basename(browser)
     133          register(browser, None, instance=controller, preferred=preferred)
     134          return [None, controller]
     135      return [None, None]
     136  
     137  
     138  # General parent classes
     139  
     140  class ESC[4;38;5;81mBaseBrowser(ESC[4;38;5;149mobject):
     141      """Parent class for all browsers. Do not use directly."""
     142  
     143      args = ['%s']
     144  
     145      def __init__(self, name=""):
     146          self.name = name
     147          self.basename = name
     148  
     149      def open(self, url, new=0, autoraise=True):
     150          raise NotImplementedError
     151  
     152      def open_new(self, url):
     153          return self.open(url, 1)
     154  
     155      def open_new_tab(self, url):
     156          return self.open(url, 2)
     157  
     158  
     159  class ESC[4;38;5;81mGenericBrowser(ESC[4;38;5;149mBaseBrowser):
     160      """Class for all browsers started with a command
     161         and without remote functionality."""
     162  
     163      def __init__(self, name):
     164          if isinstance(name, str):
     165              self.name = name
     166              self.args = ["%s"]
     167          else:
     168              # name should be a list with arguments
     169              self.name = name[0]
     170              self.args = name[1:]
     171          self.basename = os.path.basename(self.name)
     172  
     173      def open(self, url, new=0, autoraise=True):
     174          sys.audit("webbrowser.open", url)
     175          cmdline = [self.name] + [arg.replace("%s", url)
     176                                   for arg in self.args]
     177          try:
     178              if sys.platform[:3] == 'win':
     179                  p = subprocess.Popen(cmdline)
     180              else:
     181                  p = subprocess.Popen(cmdline, close_fds=True)
     182              return not p.wait()
     183          except OSError:
     184              return False
     185  
     186  
     187  class ESC[4;38;5;81mBackgroundBrowser(ESC[4;38;5;149mGenericBrowser):
     188      """Class for all browsers which are to be started in the
     189         background."""
     190  
     191      def open(self, url, new=0, autoraise=True):
     192          cmdline = [self.name] + [arg.replace("%s", url)
     193                                   for arg in self.args]
     194          sys.audit("webbrowser.open", url)
     195          try:
     196              if sys.platform[:3] == 'win':
     197                  p = subprocess.Popen(cmdline)
     198              else:
     199                  p = subprocess.Popen(cmdline, close_fds=True,
     200                                       start_new_session=True)
     201              return (p.poll() is None)
     202          except OSError:
     203              return False
     204  
     205  
     206  class ESC[4;38;5;81mUnixBrowser(ESC[4;38;5;149mBaseBrowser):
     207      """Parent class for all Unix browsers with remote functionality."""
     208  
     209      raise_opts = None
     210      background = False
     211      redirect_stdout = True
     212      # In remote_args, %s will be replaced with the requested URL.  %action will
     213      # be replaced depending on the value of 'new' passed to open.
     214      # remote_action is used for new=0 (open).  If newwin is not None, it is
     215      # used for new=1 (open_new).  If newtab is not None, it is used for
     216      # new=3 (open_new_tab).  After both substitutions are made, any empty
     217      # strings in the transformed remote_args list will be removed.
     218      remote_args = ['%action', '%s']
     219      remote_action = None
     220      remote_action_newwin = None
     221      remote_action_newtab = None
     222  
     223      def _invoke(self, args, remote, autoraise, url=None):
     224          raise_opt = []
     225          if remote and self.raise_opts:
     226              # use autoraise argument only for remote invocation
     227              autoraise = int(autoraise)
     228              opt = self.raise_opts[autoraise]
     229              if opt: raise_opt = [opt]
     230  
     231          cmdline = [self.name] + raise_opt + args
     232  
     233          if remote or self.background:
     234              inout = subprocess.DEVNULL
     235          else:
     236              # for TTY browsers, we need stdin/out
     237              inout = None
     238          p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
     239                               stdout=(self.redirect_stdout and inout or None),
     240                               stderr=inout, start_new_session=True)
     241          if remote:
     242              # wait at most five seconds. If the subprocess is not finished, the
     243              # remote invocation has (hopefully) started a new instance.
     244              try:
     245                  rc = p.wait(5)
     246                  # if remote call failed, open() will try direct invocation
     247                  return not rc
     248              except subprocess.TimeoutExpired:
     249                  return True
     250          elif self.background:
     251              if p.poll() is None:
     252                  return True
     253              else:
     254                  return False
     255          else:
     256              return not p.wait()
     257  
     258      def open(self, url, new=0, autoraise=True):
     259          sys.audit("webbrowser.open", url)
     260          if new == 0:
     261              action = self.remote_action
     262          elif new == 1:
     263              action = self.remote_action_newwin
     264          elif new == 2:
     265              if self.remote_action_newtab is None:
     266                  action = self.remote_action_newwin
     267              else:
     268                  action = self.remote_action_newtab
     269          else:
     270              raise Error("Bad 'new' parameter to open(); " +
     271                          "expected 0, 1, or 2, got %s" % new)
     272  
     273          args = [arg.replace("%s", url).replace("%action", action)
     274                  for arg in self.remote_args]
     275          args = [arg for arg in args if arg]
     276          success = self._invoke(args, True, autoraise, url)
     277          if not success:
     278              # remote invocation failed, try straight way
     279              args = [arg.replace("%s", url) for arg in self.args]
     280              return self._invoke(args, False, False)
     281          else:
     282              return True
     283  
     284  
     285  class ESC[4;38;5;81mMozilla(ESC[4;38;5;149mUnixBrowser):
     286      """Launcher class for Mozilla browsers."""
     287  
     288      remote_args = ['%action', '%s']
     289      remote_action = ""
     290      remote_action_newwin = "-new-window"
     291      remote_action_newtab = "-new-tab"
     292      background = True
     293  
     294  
     295  class ESC[4;38;5;81mEpiphany(ESC[4;38;5;149mUnixBrowser):
     296      """Launcher class for Epiphany browser."""
     297  
     298      raise_opts = ["-noraise", ""]
     299      remote_args = ['%action', '%s']
     300      remote_action = "-n"
     301      remote_action_newwin = "-w"
     302      background = True
     303  
     304  
     305  class ESC[4;38;5;81mChrome(ESC[4;38;5;149mUnixBrowser):
     306      "Launcher class for Google Chrome browser."
     307  
     308      remote_args = ['%action', '%s']
     309      remote_action = ""
     310      remote_action_newwin = "--new-window"
     311      remote_action_newtab = ""
     312      background = True
     313  
     314  Chromium = Chrome
     315  
     316  
     317  class ESC[4;38;5;81mOpera(ESC[4;38;5;149mUnixBrowser):
     318      "Launcher class for Opera browser."
     319  
     320      remote_args = ['%action', '%s']
     321      remote_action = ""
     322      remote_action_newwin = "--new-window"
     323      remote_action_newtab = ""
     324      background = True
     325  
     326  
     327  class ESC[4;38;5;81mElinks(ESC[4;38;5;149mUnixBrowser):
     328      "Launcher class for Elinks browsers."
     329  
     330      remote_args = ['-remote', 'openURL(%s%action)']
     331      remote_action = ""
     332      remote_action_newwin = ",new-window"
     333      remote_action_newtab = ",new-tab"
     334      background = False
     335  
     336      # elinks doesn't like its stdout to be redirected -
     337      # it uses redirected stdout as a signal to do -dump
     338      redirect_stdout = False
     339  
     340  
     341  class ESC[4;38;5;81mKonqueror(ESC[4;38;5;149mBaseBrowser):
     342      """Controller for the KDE File Manager (kfm, or Konqueror).
     343  
     344      See the output of ``kfmclient --commands``
     345      for more information on the Konqueror remote-control interface.
     346      """
     347  
     348      def open(self, url, new=0, autoraise=True):
     349          sys.audit("webbrowser.open", url)
     350          # XXX Currently I know no way to prevent KFM from opening a new win.
     351          if new == 2:
     352              action = "newTab"
     353          else:
     354              action = "openURL"
     355  
     356          devnull = subprocess.DEVNULL
     357  
     358          try:
     359              p = subprocess.Popen(["kfmclient", action, url],
     360                                   close_fds=True, stdin=devnull,
     361                                   stdout=devnull, stderr=devnull)
     362          except OSError:
     363              # fall through to next variant
     364              pass
     365          else:
     366              p.wait()
     367              # kfmclient's return code unfortunately has no meaning as it seems
     368              return True
     369  
     370          try:
     371              p = subprocess.Popen(["konqueror", "--silent", url],
     372                                   close_fds=True, stdin=devnull,
     373                                   stdout=devnull, stderr=devnull,
     374                                   start_new_session=True)
     375          except OSError:
     376              # fall through to next variant
     377              pass
     378          else:
     379              if p.poll() is None:
     380                  # Should be running now.
     381                  return True
     382  
     383          try:
     384              p = subprocess.Popen(["kfm", "-d", url],
     385                                   close_fds=True, stdin=devnull,
     386                                   stdout=devnull, stderr=devnull,
     387                                   start_new_session=True)
     388          except OSError:
     389              return False
     390          else:
     391              return (p.poll() is None)
     392  
     393  
     394  class ESC[4;38;5;81mEdge(ESC[4;38;5;149mUnixBrowser):
     395      "Launcher class for Microsoft Edge browser."
     396  
     397      remote_args = ['%action', '%s']
     398      remote_action = ""
     399      remote_action_newwin = "--new-window"
     400      remote_action_newtab = ""
     401      background = True
     402  
     403  
     404  #
     405  # Platform support for Unix
     406  #
     407  
     408  # These are the right tests because all these Unix browsers require either
     409  # a console terminal or an X display to run.
     410  
     411  def register_X_browsers():
     412  
     413      # use xdg-open if around
     414      if shutil.which("xdg-open"):
     415          register("xdg-open", None, BackgroundBrowser("xdg-open"))
     416  
     417      # Opens an appropriate browser for the URL scheme according to
     418      # freedesktop.org settings (GNOME, KDE, XFCE, etc.)
     419      if shutil.which("gio"):
     420          register("gio", None, BackgroundBrowser(["gio", "open", "--", "%s"]))
     421  
     422      # Equivalent of gio open before 2015
     423      if "GNOME_DESKTOP_SESSION_ID" in os.environ and shutil.which("gvfs-open"):
     424          register("gvfs-open", None, BackgroundBrowser("gvfs-open"))
     425  
     426      # The default KDE browser
     427      if "KDE_FULL_SESSION" in os.environ and shutil.which("kfmclient"):
     428          register("kfmclient", Konqueror, Konqueror("kfmclient"))
     429  
     430      # Common symbolic link for the default X11 browser
     431      if shutil.which("x-www-browser"):
     432          register("x-www-browser", None, BackgroundBrowser("x-www-browser"))
     433  
     434      # The Mozilla browsers
     435      for browser in ("firefox", "iceweasel", "seamonkey", "mozilla-firefox",
     436                      "mozilla"):
     437          if shutil.which(browser):
     438              register(browser, None, Mozilla(browser))
     439  
     440      # Konqueror/kfm, the KDE browser.
     441      if shutil.which("kfm"):
     442          register("kfm", Konqueror, Konqueror("kfm"))
     443      elif shutil.which("konqueror"):
     444          register("konqueror", Konqueror, Konqueror("konqueror"))
     445  
     446      # Gnome's Epiphany
     447      if shutil.which("epiphany"):
     448          register("epiphany", None, Epiphany("epiphany"))
     449  
     450      # Google Chrome/Chromium browsers
     451      for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"):
     452          if shutil.which(browser):
     453              register(browser, None, Chrome(browser))
     454  
     455      # Opera, quite popular
     456      if shutil.which("opera"):
     457          register("opera", None, Opera("opera"))
     458  
     459  
     460      if shutil.which("microsoft-edge"):
     461          register("microsoft-edge", None, Edge("microsoft-edge"))
     462  
     463  
     464  def register_standard_browsers():
     465      global _tryorder
     466      _tryorder = []
     467  
     468      if sys.platform == 'darwin':
     469          register("MacOSX", None, MacOSXOSAScript('default'))
     470          register("chrome", None, MacOSXOSAScript('chrome'))
     471          register("firefox", None, MacOSXOSAScript('firefox'))
     472          register("safari", None, MacOSXOSAScript('safari'))
     473          # OS X can use below Unix support (but we prefer using the OS X
     474          # specific stuff)
     475  
     476      if sys.platform == "serenityos":
     477          # SerenityOS webbrowser, simply called "Browser".
     478          register("Browser", None, BackgroundBrowser("Browser"))
     479  
     480      if sys.platform[:3] == "win":
     481          # First try to use the default Windows browser
     482          register("windows-default", WindowsDefault)
     483  
     484          # Detect some common Windows browsers, fallback to Microsoft Edge
     485          # location in 64-bit Windows
     486          edge64 = os.path.join(os.environ.get("PROGRAMFILES(x86)", "C:\\Program Files (x86)"),
     487                                "Microsoft\\Edge\\Application\\msedge.exe")
     488          # location in 32-bit Windows
     489          edge32 = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
     490                                "Microsoft\\Edge\\Application\\msedge.exe")
     491          for browser in ("firefox", "seamonkey", "mozilla", "chrome",
     492                          "opera", edge64, edge32):
     493              if shutil.which(browser):
     494                  register(browser, None, BackgroundBrowser(browser))
     495          if shutil.which("MicrosoftEdge.exe"):
     496              register("microsoft-edge", None, Edge("MicrosoftEdge.exe"))
     497      else:
     498          # Prefer X browsers if present
     499          if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"):
     500              try:
     501                  cmd = "xdg-settings get default-web-browser".split()
     502                  raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
     503                  result = raw_result.decode().strip()
     504              except (FileNotFoundError, subprocess.CalledProcessError, PermissionError, NotADirectoryError) :
     505                  pass
     506              else:
     507                  global _os_preferred_browser
     508                  _os_preferred_browser = result
     509  
     510              register_X_browsers()
     511  
     512          # Also try console browsers
     513          if os.environ.get("TERM"):
     514              # Common symbolic link for the default text-based browser
     515              if shutil.which("www-browser"):
     516                  register("www-browser", None, GenericBrowser("www-browser"))
     517              # The Links/elinks browsers <http://links.twibright.com/>
     518              if shutil.which("links"):
     519                  register("links", None, GenericBrowser("links"))
     520              if shutil.which("elinks"):
     521                  register("elinks", None, Elinks("elinks"))
     522              # The Lynx browser <https://lynx.invisible-island.net/>, <http://lynx.browser.org/>
     523              if shutil.which("lynx"):
     524                  register("lynx", None, GenericBrowser("lynx"))
     525              # The w3m browser <http://w3m.sourceforge.net/>
     526              if shutil.which("w3m"):
     527                  register("w3m", None, GenericBrowser("w3m"))
     528  
     529      # OK, now that we know what the default preference orders for each
     530      # platform are, allow user to override them with the BROWSER variable.
     531      if "BROWSER" in os.environ:
     532          userchoices = os.environ["BROWSER"].split(os.pathsep)
     533          userchoices.reverse()
     534  
     535          # Treat choices in same way as if passed into get() but do register
     536          # and prepend to _tryorder
     537          for cmdline in userchoices:
     538              if cmdline != '':
     539                  cmd = _synthesize(cmdline, preferred=True)
     540                  if cmd[1] is None:
     541                      register(cmdline, None, GenericBrowser(cmdline), preferred=True)
     542  
     543      # what to do if _tryorder is now empty?
     544  
     545  
     546  #
     547  # Platform support for Windows
     548  #
     549  
     550  if sys.platform[:3] == "win":
     551      class ESC[4;38;5;81mWindowsDefault(ESC[4;38;5;149mBaseBrowser):
     552          def open(self, url, new=0, autoraise=True):
     553              sys.audit("webbrowser.open", url)
     554              try:
     555                  os.startfile(url)
     556              except OSError:
     557                  # [Error 22] No application is associated with the specified
     558                  # file for this operation: '<URL>'
     559                  return False
     560              else:
     561                  return True
     562  
     563  #
     564  # Platform support for MacOS
     565  #
     566  
     567  if sys.platform == 'darwin':
     568      # Adapted from patch submitted to SourceForge by Steven J. Burr
     569      class ESC[4;38;5;81mMacOSX(ESC[4;38;5;149mBaseBrowser):
     570          """Launcher class for Aqua browsers on Mac OS X
     571  
     572          Optionally specify a browser name on instantiation.  Note that this
     573          will not work for Aqua browsers if the user has moved the application
     574          package after installation.
     575  
     576          If no browser is specified, the default browser, as specified in the
     577          Internet System Preferences panel, will be used.
     578          """
     579          def __init__(self, name):
     580              warnings.warn(f'{self.__class__.__name__} is deprecated in 3.11'
     581                            ' use MacOSXOSAScript instead.', DeprecationWarning, stacklevel=2)
     582              self.name = name
     583  
     584          def open(self, url, new=0, autoraise=True):
     585              sys.audit("webbrowser.open", url)
     586              assert "'" not in url
     587              # hack for local urls
     588              if not ':' in url:
     589                  url = 'file:'+url
     590  
     591              # new must be 0 or 1
     592              new = int(bool(new))
     593              if self.name == "default":
     594                  # User called open, open_new or get without a browser parameter
     595                  script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
     596              else:
     597                  # User called get and chose a browser
     598                  if self.name == "OmniWeb":
     599                      toWindow = ""
     600                  else:
     601                      # Include toWindow parameter of OpenURL command for browsers
     602                      # that support it.  0 == new window; -1 == existing
     603                      toWindow = "toWindow %d" % (new - 1)
     604                  cmd = 'OpenURL "%s"' % url.replace('"', '%22')
     605                  script = '''tell application "%s"
     606                                  activate
     607                                  %s %s
     608                              end tell''' % (self.name, cmd, toWindow)
     609              # Open pipe to AppleScript through osascript command
     610              osapipe = os.popen("osascript", "w")
     611              if osapipe is None:
     612                  return False
     613              # Write script to osascript's stdin
     614              osapipe.write(script)
     615              rc = osapipe.close()
     616              return not rc
     617  
     618      class ESC[4;38;5;81mMacOSXOSAScript(ESC[4;38;5;149mBaseBrowser):
     619          def __init__(self, name='default'):
     620              super().__init__(name)
     621  
     622          @property
     623          def _name(self):
     624              warnings.warn(f'{self.__class__.__name__}._name is deprecated in 3.11'
     625                            f' use {self.__class__.__name__}.name instead.',
     626                            DeprecationWarning, stacklevel=2)
     627              return self.name
     628  
     629          @_name.setter
     630          def _name(self, val):
     631              warnings.warn(f'{self.__class__.__name__}._name is deprecated in 3.11'
     632                            f' use {self.__class__.__name__}.name instead.',
     633                            DeprecationWarning, stacklevel=2)
     634              self.name = val
     635  
     636          def open(self, url, new=0, autoraise=True):
     637              if self.name == 'default':
     638                  script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
     639              else:
     640                  script = f'''
     641                     tell application "%s"
     642                         activate
     643                         open location "%s"
     644                     end
     645                     '''%(self.name, url.replace('"', '%22'))
     646  
     647              osapipe = os.popen("osascript", "w")
     648              if osapipe is None:
     649                  return False
     650  
     651              osapipe.write(script)
     652              rc = osapipe.close()
     653              return not rc
     654  
     655  
     656  def main():
     657      import getopt
     658      usage = """Usage: %s [-n | -t | -h] url
     659      -n: open new window
     660      -t: open new tab
     661      -h, --help: show help""" % sys.argv[0]
     662      try:
     663          opts, args = getopt.getopt(sys.argv[1:], 'ntdh',['help'])
     664      except getopt.error as msg:
     665          print(msg, file=sys.stderr)
     666          print(usage, file=sys.stderr)
     667          sys.exit(1)
     668      new_win = 0
     669      for o, a in opts:
     670          if o == '-n': new_win = 1
     671          elif o == '-t': new_win = 2
     672          elif o == '-h' or o == '--help':
     673              print(usage, file=sys.stderr)
     674              sys.exit()
     675      if len(args) != 1:
     676          print(usage, file=sys.stderr)
     677          sys.exit(1)
     678  
     679      url = args[0]
     680      open(url, new_win)
     681  
     682      print("\a")
     683  
     684  if __name__ == "__main__":
     685      main()