(root)/
Python-3.11.7/
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;81mNetscape(ESC[4;38;5;149mUnixBrowser):
     296      """Launcher class for Netscape browser."""
     297  
     298      raise_opts = ["-noraise", "-raise"]
     299      remote_args = ['-remote', 'openURL(%s%action)']
     300      remote_action = ""
     301      remote_action_newwin = ",new-window"
     302      remote_action_newtab = ",new-tab"
     303      background = True
     304  
     305  
     306  class ESC[4;38;5;81mGaleon(ESC[4;38;5;149mUnixBrowser):
     307      """Launcher class for Galeon/Epiphany browsers."""
     308  
     309      raise_opts = ["-noraise", ""]
     310      remote_args = ['%action', '%s']
     311      remote_action = "-n"
     312      remote_action_newwin = "-w"
     313      background = True
     314  
     315  
     316  class ESC[4;38;5;81mChrome(ESC[4;38;5;149mUnixBrowser):
     317      "Launcher class for Google Chrome browser."
     318  
     319      remote_args = ['%action', '%s']
     320      remote_action = ""
     321      remote_action_newwin = "--new-window"
     322      remote_action_newtab = ""
     323      background = True
     324  
     325  Chromium = Chrome
     326  
     327  
     328  class ESC[4;38;5;81mOpera(ESC[4;38;5;149mUnixBrowser):
     329      "Launcher class for Opera browser."
     330  
     331      remote_args = ['%action', '%s']
     332      remote_action = ""
     333      remote_action_newwin = "--new-window"
     334      remote_action_newtab = ""
     335      background = True
     336  
     337  
     338  class ESC[4;38;5;81mElinks(ESC[4;38;5;149mUnixBrowser):
     339      "Launcher class for Elinks browsers."
     340  
     341      remote_args = ['-remote', 'openURL(%s%action)']
     342      remote_action = ""
     343      remote_action_newwin = ",new-window"
     344      remote_action_newtab = ",new-tab"
     345      background = False
     346  
     347      # elinks doesn't like its stdout to be redirected -
     348      # it uses redirected stdout as a signal to do -dump
     349      redirect_stdout = False
     350  
     351  
     352  class ESC[4;38;5;81mKonqueror(ESC[4;38;5;149mBaseBrowser):
     353      """Controller for the KDE File Manager (kfm, or Konqueror).
     354  
     355      See the output of ``kfmclient --commands``
     356      for more information on the Konqueror remote-control interface.
     357      """
     358  
     359      def open(self, url, new=0, autoraise=True):
     360          sys.audit("webbrowser.open", url)
     361          # XXX Currently I know no way to prevent KFM from opening a new win.
     362          if new == 2:
     363              action = "newTab"
     364          else:
     365              action = "openURL"
     366  
     367          devnull = subprocess.DEVNULL
     368  
     369          try:
     370              p = subprocess.Popen(["kfmclient", action, url],
     371                                   close_fds=True, stdin=devnull,
     372                                   stdout=devnull, stderr=devnull)
     373          except OSError:
     374              # fall through to next variant
     375              pass
     376          else:
     377              p.wait()
     378              # kfmclient's return code unfortunately has no meaning as it seems
     379              return True
     380  
     381          try:
     382              p = subprocess.Popen(["konqueror", "--silent", url],
     383                                   close_fds=True, stdin=devnull,
     384                                   stdout=devnull, stderr=devnull,
     385                                   start_new_session=True)
     386          except OSError:
     387              # fall through to next variant
     388              pass
     389          else:
     390              if p.poll() is None:
     391                  # Should be running now.
     392                  return True
     393  
     394          try:
     395              p = subprocess.Popen(["kfm", "-d", url],
     396                                   close_fds=True, stdin=devnull,
     397                                   stdout=devnull, stderr=devnull,
     398                                   start_new_session=True)
     399          except OSError:
     400              return False
     401          else:
     402              return (p.poll() is None)
     403  
     404  
     405  class ESC[4;38;5;81mGrail(ESC[4;38;5;149mBaseBrowser):
     406      # There should be a way to maintain a connection to Grail, but the
     407      # Grail remote control protocol doesn't really allow that at this
     408      # point.  It probably never will!
     409      def _find_grail_rc(self):
     410          import glob
     411          import pwd
     412          import socket
     413          import tempfile
     414          tempdir = os.path.join(tempfile.gettempdir(),
     415                                 ".grail-unix")
     416          user = pwd.getpwuid(os.getuid())[0]
     417          filename = os.path.join(glob.escape(tempdir), glob.escape(user) + "-*")
     418          maybes = glob.glob(filename)
     419          if not maybes:
     420              return None
     421          s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
     422          for fn in maybes:
     423              # need to PING each one until we find one that's live
     424              try:
     425                  s.connect(fn)
     426              except OSError:
     427                  # no good; attempt to clean it out, but don't fail:
     428                  try:
     429                      os.unlink(fn)
     430                  except OSError:
     431                      pass
     432              else:
     433                  return s
     434  
     435      def _remote(self, action):
     436          s = self._find_grail_rc()
     437          if not s:
     438              return 0
     439          s.send(action)
     440          s.close()
     441          return 1
     442  
     443      def open(self, url, new=0, autoraise=True):
     444          sys.audit("webbrowser.open", url)
     445          if new:
     446              ok = self._remote("LOADNEW " + url)
     447          else:
     448              ok = self._remote("LOAD " + url)
     449          return ok
     450  
     451  
     452  #
     453  # Platform support for Unix
     454  #
     455  
     456  # These are the right tests because all these Unix browsers require either
     457  # a console terminal or an X display to run.
     458  
     459  def register_X_browsers():
     460  
     461      # use xdg-open if around
     462      if shutil.which("xdg-open"):
     463          register("xdg-open", None, BackgroundBrowser("xdg-open"))
     464  
     465      # Opens an appropriate browser for the URL scheme according to
     466      # freedesktop.org settings (GNOME, KDE, XFCE, etc.)
     467      if shutil.which("gio"):
     468          register("gio", None, BackgroundBrowser(["gio", "open", "--", "%s"]))
     469  
     470      # Equivalent of gio open before 2015
     471      if "GNOME_DESKTOP_SESSION_ID" in os.environ and shutil.which("gvfs-open"):
     472          register("gvfs-open", None, BackgroundBrowser("gvfs-open"))
     473  
     474      # The default KDE browser
     475      if "KDE_FULL_SESSION" in os.environ and shutil.which("kfmclient"):
     476          register("kfmclient", Konqueror, Konqueror("kfmclient"))
     477  
     478      if shutil.which("x-www-browser"):
     479          register("x-www-browser", None, BackgroundBrowser("x-www-browser"))
     480  
     481      # The Mozilla browsers
     482      for browser in ("firefox", "iceweasel", "iceape", "seamonkey"):
     483          if shutil.which(browser):
     484              register(browser, None, Mozilla(browser))
     485  
     486      # The Netscape and old Mozilla browsers
     487      for browser in ("mozilla-firefox",
     488                      "mozilla-firebird", "firebird",
     489                      "mozilla", "netscape"):
     490          if shutil.which(browser):
     491              register(browser, None, Netscape(browser))
     492  
     493      # Konqueror/kfm, the KDE browser.
     494      if shutil.which("kfm"):
     495          register("kfm", Konqueror, Konqueror("kfm"))
     496      elif shutil.which("konqueror"):
     497          register("konqueror", Konqueror, Konqueror("konqueror"))
     498  
     499      # Gnome's Galeon and Epiphany
     500      for browser in ("galeon", "epiphany"):
     501          if shutil.which(browser):
     502              register(browser, None, Galeon(browser))
     503  
     504      # Skipstone, another Gtk/Mozilla based browser
     505      if shutil.which("skipstone"):
     506          register("skipstone", None, BackgroundBrowser("skipstone"))
     507  
     508      # Google Chrome/Chromium browsers
     509      for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"):
     510          if shutil.which(browser):
     511              register(browser, None, Chrome(browser))
     512  
     513      # Opera, quite popular
     514      if shutil.which("opera"):
     515          register("opera", None, Opera("opera"))
     516  
     517      # Next, Mosaic -- old but still in use.
     518      if shutil.which("mosaic"):
     519          register("mosaic", None, BackgroundBrowser("mosaic"))
     520  
     521      # Grail, the Python browser. Does anybody still use it?
     522      if shutil.which("grail"):
     523          register("grail", Grail, None)
     524  
     525  def register_standard_browsers():
     526      global _tryorder
     527      _tryorder = []
     528  
     529      if sys.platform == 'darwin':
     530          register("MacOSX", None, MacOSXOSAScript('default'))
     531          register("chrome", None, MacOSXOSAScript('chrome'))
     532          register("firefox", None, MacOSXOSAScript('firefox'))
     533          register("safari", None, MacOSXOSAScript('safari'))
     534          # OS X can use below Unix support (but we prefer using the OS X
     535          # specific stuff)
     536  
     537      if sys.platform == "serenityos":
     538          # SerenityOS webbrowser, simply called "Browser".
     539          register("Browser", None, BackgroundBrowser("Browser"))
     540  
     541      if sys.platform[:3] == "win":
     542          # First try to use the default Windows browser
     543          register("windows-default", WindowsDefault)
     544  
     545          # Detect some common Windows browsers, fallback to IE
     546          iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
     547                                  "Internet Explorer\\IEXPLORE.EXE")
     548          for browser in ("firefox", "firebird", "seamonkey", "mozilla",
     549                          "netscape", "opera", iexplore):
     550              if shutil.which(browser):
     551                  register(browser, None, BackgroundBrowser(browser))
     552      else:
     553          # Prefer X browsers if present
     554          if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"):
     555              try:
     556                  cmd = "xdg-settings get default-web-browser".split()
     557                  raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
     558                  result = raw_result.decode().strip()
     559              except (FileNotFoundError, subprocess.CalledProcessError, PermissionError, NotADirectoryError) :
     560                  pass
     561              else:
     562                  global _os_preferred_browser
     563                  _os_preferred_browser = result
     564  
     565              register_X_browsers()
     566  
     567          # Also try console browsers
     568          if os.environ.get("TERM"):
     569              if shutil.which("www-browser"):
     570                  register("www-browser", None, GenericBrowser("www-browser"))
     571              # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
     572              if shutil.which("links"):
     573                  register("links", None, GenericBrowser("links"))
     574              if shutil.which("elinks"):
     575                  register("elinks", None, Elinks("elinks"))
     576              # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
     577              if shutil.which("lynx"):
     578                  register("lynx", None, GenericBrowser("lynx"))
     579              # The w3m browser <http://w3m.sourceforge.net/>
     580              if shutil.which("w3m"):
     581                  register("w3m", None, GenericBrowser("w3m"))
     582  
     583      # OK, now that we know what the default preference orders for each
     584      # platform are, allow user to override them with the BROWSER variable.
     585      if "BROWSER" in os.environ:
     586          userchoices = os.environ["BROWSER"].split(os.pathsep)
     587          userchoices.reverse()
     588  
     589          # Treat choices in same way as if passed into get() but do register
     590          # and prepend to _tryorder
     591          for cmdline in userchoices:
     592              if cmdline != '':
     593                  cmd = _synthesize(cmdline, preferred=True)
     594                  if cmd[1] is None:
     595                      register(cmdline, None, GenericBrowser(cmdline), preferred=True)
     596  
     597      # what to do if _tryorder is now empty?
     598  
     599  
     600  #
     601  # Platform support for Windows
     602  #
     603  
     604  if sys.platform[:3] == "win":
     605      class ESC[4;38;5;81mWindowsDefault(ESC[4;38;5;149mBaseBrowser):
     606          def open(self, url, new=0, autoraise=True):
     607              sys.audit("webbrowser.open", url)
     608              try:
     609                  os.startfile(url)
     610              except OSError:
     611                  # [Error 22] No application is associated with the specified
     612                  # file for this operation: '<URL>'
     613                  return False
     614              else:
     615                  return True
     616  
     617  #
     618  # Platform support for MacOS
     619  #
     620  
     621  if sys.platform == 'darwin':
     622      # Adapted from patch submitted to SourceForge by Steven J. Burr
     623      class ESC[4;38;5;81mMacOSX(ESC[4;38;5;149mBaseBrowser):
     624          """Launcher class for Aqua browsers on Mac OS X
     625  
     626          Optionally specify a browser name on instantiation.  Note that this
     627          will not work for Aqua browsers if the user has moved the application
     628          package after installation.
     629  
     630          If no browser is specified, the default browser, as specified in the
     631          Internet System Preferences panel, will be used.
     632          """
     633          def __init__(self, name):
     634              warnings.warn(f'{self.__class__.__name__} is deprecated in 3.11'
     635                            ' use MacOSXOSAScript instead.', DeprecationWarning, stacklevel=2)
     636              self.name = name
     637  
     638          def open(self, url, new=0, autoraise=True):
     639              sys.audit("webbrowser.open", url)
     640              assert "'" not in url
     641              # hack for local urls
     642              if not ':' in url:
     643                  url = 'file:'+url
     644  
     645              # new must be 0 or 1
     646              new = int(bool(new))
     647              if self.name == "default":
     648                  # User called open, open_new or get without a browser parameter
     649                  script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
     650              else:
     651                  # User called get and chose a browser
     652                  if self.name == "OmniWeb":
     653                      toWindow = ""
     654                  else:
     655                      # Include toWindow parameter of OpenURL command for browsers
     656                      # that support it.  0 == new window; -1 == existing
     657                      toWindow = "toWindow %d" % (new - 1)
     658                  cmd = 'OpenURL "%s"' % url.replace('"', '%22')
     659                  script = '''tell application "%s"
     660                                  activate
     661                                  %s %s
     662                              end tell''' % (self.name, cmd, toWindow)
     663              # Open pipe to AppleScript through osascript command
     664              osapipe = os.popen("osascript", "w")
     665              if osapipe is None:
     666                  return False
     667              # Write script to osascript's stdin
     668              osapipe.write(script)
     669              rc = osapipe.close()
     670              return not rc
     671  
     672      class ESC[4;38;5;81mMacOSXOSAScript(ESC[4;38;5;149mBaseBrowser):
     673          def __init__(self, name='default'):
     674              super().__init__(name)
     675  
     676          @property
     677          def _name(self):
     678              warnings.warn(f'{self.__class__.__name__}._name is deprecated in 3.11'
     679                            f' use {self.__class__.__name__}.name instead.',
     680                            DeprecationWarning, stacklevel=2)
     681              return self.name
     682  
     683          @_name.setter
     684          def _name(self, val):
     685              warnings.warn(f'{self.__class__.__name__}._name is deprecated in 3.11'
     686                            f' use {self.__class__.__name__}.name instead.',
     687                            DeprecationWarning, stacklevel=2)
     688              self.name = val
     689  
     690          def open(self, url, new=0, autoraise=True):
     691              if self.name == 'default':
     692                  script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
     693              else:
     694                  script = f'''
     695                     tell application "%s"
     696                         activate
     697                         open location "%s"
     698                     end
     699                     '''%(self.name, url.replace('"', '%22'))
     700  
     701              osapipe = os.popen("osascript", "w")
     702              if osapipe is None:
     703                  return False
     704  
     705              osapipe.write(script)
     706              rc = osapipe.close()
     707              return not rc
     708  
     709  
     710  def main():
     711      import getopt
     712      usage = """Usage: %s [-n | -t] url
     713      -n: open new window
     714      -t: open new tab""" % sys.argv[0]
     715      try:
     716          opts, args = getopt.getopt(sys.argv[1:], 'ntd')
     717      except getopt.error as msg:
     718          print(msg, file=sys.stderr)
     719          print(usage, file=sys.stderr)
     720          sys.exit(1)
     721      new_win = 0
     722      for o, a in opts:
     723          if o == '-n': new_win = 1
     724          elif o == '-t': new_win = 2
     725      if len(args) != 1:
     726          print(usage, file=sys.stderr)
     727          sys.exit(1)
     728  
     729      url = args[0]
     730      open(url, new_win)
     731  
     732      print("\a")
     733  
     734  if __name__ == "__main__":
     735      main()