]> granicus.if.org Git - python/commitdiff
bpo-24241: Improve preferred webbrowser handling (#85)
authorDavid Steele <davesteele@users.noreply.github.com>
Sat, 25 Feb 2017 04:47:38 +0000 (23:47 -0500)
committerNick Coghlan <ncoghlan@gmail.com>
Sat, 25 Feb 2017 04:47:38 +0000 (14:47 +1000)
- Add 'preferred' argument to webbrowser.register
- Use xdg-settings to specify preferred X browser

The first change replaces the existing undocumented tri-state
'try_order' parameter with the documented boolean keyword-only
'preferred' parameter. Setting it to True places the browser at the
front of the list, preferring it as the return to a subsequent get() call.

The second change adds a private `_os_preferred_browser` setting
and then uses that to make the default browser reported by
`xdg-settings` first in the try list when running under X (or
another environment that sets the `DISPLAY` variable).
This avoids the problem where the first entry in the tryorder
queue otherwise defaults to xdg-open, which doesn't support
the "new window" option.

Doc/library/webbrowser.rst
Lib/webbrowser.py
Misc/ACKS
Misc/NEWS

index 85d36367221295bee1227d1ec553c2b1d09a2a26..ee501e80d9ed617f0d77b79753f02b77a3c4293e 100644 (file)
@@ -83,7 +83,7 @@ The following functions are defined:
    caller's environment.
 
 
-.. function:: register(name, constructor, instance=None)
+.. function:: register(name, constructor, instance=None, *, preferred=False)
 
    Register the browser type *name*.  Once a browser type is registered, the
    :func:`get` function can return a controller for that browser type.  If
@@ -91,9 +91,11 @@ The following functions are defined:
    parameters to create an instance when needed.  If *instance* is provided,
    *constructor* will never be called, and may be ``None``.
 
-   This entry point is only useful if you plan to either set the :envvar:`BROWSER`
-   variable or call :func:`get` with a nonempty argument matching the name of a
-   handler you declare.
+   Setting *preferred* to ``True`` makes this browser a preferred result for
+   a :func:`get` call with no argument. Otherwise, this entry point is only
+   useful if you plan to either set the :envvar:`BROWSER` variable or call
+   :func:`get` with a nonempty argument matching the name of a handler you
+   declare.
 
 A number of browser types are predefined.  This table gives the type names that
 may be passed to the :func:`get` function and the corresponding instantiations
index 6f43b7f1265d1abdbb213b9e634bfa6d55122032..a9eac69650546fe91c06e2dc1e613d6891c2e583 100755 (executable)
@@ -13,16 +13,21 @@ __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
 class Error(Exception):
     pass
 
-_browsers = {}          # Dictionary of available browser controllers
-_tryorder = []          # Preference order of available browsers
+_browsers = {}                  # Dictionary of available browser controllers
+_tryorder = []                  # Preference order of available browsers
+_os_preferred_browser = None    # The preferred browser
 
-def register(name, klass, instance=None, update_tryorder=1):
-    """Register a browser connector and, optionally, connection."""
+def register(name, klass, instance=None, *, preferred=False):
+    """Register a browser connector."""
     _browsers[name.lower()] = [klass, instance]
-    if update_tryorder > 0:
-        _tryorder.append(name)
-    elif update_tryorder < 0:
+
+    # Preferred browsers go to the front of the list.
+    # Need to match to the default browser returned by xdg-settings, which
+    # may be of the form e.g. "firefox.desktop".
+    if preferred or (_os_preferred_browser and name in _os_preferred_browser):
         _tryorder.insert(0, name)
+    else:
+        _tryorder.append(name)
 
 def get(using=None):
     """Return a browser launcher instance appropriate for the environment."""
@@ -484,6 +489,14 @@ def register_X_browsers():
 
 # Prefer X browsers if present
 if os.environ.get("DISPLAY"):
+    try:
+        cmd = "xdg-settings get default-web-browser".split()
+        result = subprocess.check_output(cmd).decode().strip()
+    except (FileNotFoundError, subprocess.CalledProcessError):
+        pass
+    else:
+        _os_preferred_browser = result
+
     register_X_browsers()
 
 # Also try console browsers
@@ -610,10 +623,10 @@ if sys.platform == 'darwin':
 
     # Don't clear _tryorder or _browsers since OS X can use above Unix support
     # (but we prefer using the OS X specific stuff)
-    register("safari", None, MacOSXOSAScript('safari'), -1)
-    register("firefox", None, MacOSXOSAScript('firefox'), -1)
-    register("chrome", None, MacOSXOSAScript('chrome'), -1)
-    register("MacOSX", None, MacOSXOSAScript('default'), -1)
+    register("safari", None, MacOSXOSAScript('safari'), preferred=True)
+    register("firefox", None, MacOSXOSAScript('firefox'), preferred=True)
+    register("chrome", None, MacOSXOSAScript('chrome'), preferred=True)
+    register("MacOSX", None, MacOSXOSAScript('default'), preferred=True)
 
 
 # OK, now that we know what the default preference orders for each
@@ -628,7 +641,7 @@ if "BROWSER" in os.environ:
         if cmdline != '':
             cmd = _synthesize(cmdline, -1)
             if cmd[1] is None:
-                register(cmdline, None, GenericBrowser(cmdline), -1)
+                register(cmdline, None, GenericBrowser(cmdline), preferred=True)
     cmdline = None # to make del work if _userchoices was empty
     del cmdline
     del _userchoices
index 5ab6411688c7d16c65618931f10b73f614d642f7..e63a061098e8e00c068f833d6f4aa4b9458fd064 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1458,6 +1458,7 @@ Quentin Stafford-Fraser
 Frank Stajano
 Joel Stanley
 Anthony Starks
+David Steele
 Oliver Steele
 Greg Stein
 Marek Stepniowski
index 51560612d5a154c3665cccdbd2c9bc57579b266f..e7ab3df8d773c0afdd7114324a39d886eb9b8198 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -433,6 +433,11 @@ Library
 - Issue #23262: The webbrowser module now supports Firefox 36+ and derived
   browsers.  Based on patch by Oleg Broytman.
 
+- Issue #24241: The webbrowser in an X environment now prefers using the
+  default browser directly. Also, the webbrowser register() function now has
+  a documented 'preferred' argument, to specify browsers to be returned by
+  get() with no arguments. Patch by David Steele
+
 - Issue #27939: Fixed bugs in tkinter.ttk.LabeledScale and tkinter.Scale caused
   by representing the scale as float value internally in Tk.  tkinter.IntVar
   now works if float value is set to underlying Tk variable.