Commit e3ce6952 authored by David Steele's avatar David Steele Committed by Nick Coghlan

bpo-24241: Improve preferred webbrowser handling (#85)

- 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.
parent e3bf4cdd
...@@ -83,7 +83,7 @@ The following functions are defined: ...@@ -83,7 +83,7 @@ The following functions are defined:
caller's environment. 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 Register the browser type *name*. Once a browser type is registered, the
:func:`get` function can return a controller for that browser type. If :func:`get` function can return a controller for that browser type. If
...@@ -91,9 +91,11 @@ The following functions are defined: ...@@ -91,9 +91,11 @@ The following functions are defined:
parameters to create an instance when needed. If *instance* is provided, parameters to create an instance when needed. If *instance* is provided,
*constructor* will never be called, and may be ``None``. *constructor* will never be called, and may be ``None``.
This entry point is only useful if you plan to either set the :envvar:`BROWSER` Setting *preferred* to ``True`` makes this browser a preferred result for
variable or call :func:`get` with a nonempty argument matching the name of a a :func:`get` call with no argument. Otherwise, this entry point is only
handler you declare. 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 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 may be passed to the :func:`get` function and the corresponding instantiations
......
...@@ -13,16 +13,21 @@ __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] ...@@ -13,16 +13,21 @@ __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
class Error(Exception): class Error(Exception):
pass pass
_browsers = {} # Dictionary of available browser controllers _browsers = {} # Dictionary of available browser controllers
_tryorder = [] # Preference order of available browsers _tryorder = [] # Preference order of available browsers
_os_preferred_browser = None # The preferred browser
def register(name, klass, instance=None, update_tryorder=1): def register(name, klass, instance=None, *, preferred=False):
"""Register a browser connector and, optionally, connection.""" """Register a browser connector."""
_browsers[name.lower()] = [klass, instance] _browsers[name.lower()] = [klass, instance]
if update_tryorder > 0:
_tryorder.append(name) # Preferred browsers go to the front of the list.
elif update_tryorder < 0: # 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) _tryorder.insert(0, name)
else:
_tryorder.append(name)
def get(using=None): def get(using=None):
"""Return a browser launcher instance appropriate for the environment.""" """Return a browser launcher instance appropriate for the environment."""
...@@ -484,6 +489,14 @@ def register_X_browsers(): ...@@ -484,6 +489,14 @@ def register_X_browsers():
# Prefer X browsers if present # Prefer X browsers if present
if os.environ.get("DISPLAY"): 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() register_X_browsers()
# Also try console browsers # Also try console browsers
...@@ -610,10 +623,10 @@ if sys.platform == 'darwin': ...@@ -610,10 +623,10 @@ if sys.platform == 'darwin':
# Don't clear _tryorder or _browsers since OS X can use above Unix support # Don't clear _tryorder or _browsers since OS X can use above Unix support
# (but we prefer using the OS X specific stuff) # (but we prefer using the OS X specific stuff)
register("safari", None, MacOSXOSAScript('safari'), -1) register("safari", None, MacOSXOSAScript('safari'), preferred=True)
register("firefox", None, MacOSXOSAScript('firefox'), -1) register("firefox", None, MacOSXOSAScript('firefox'), preferred=True)
register("chrome", None, MacOSXOSAScript('chrome'), -1) register("chrome", None, MacOSXOSAScript('chrome'), preferred=True)
register("MacOSX", None, MacOSXOSAScript('default'), -1) register("MacOSX", None, MacOSXOSAScript('default'), preferred=True)
# OK, now that we know what the default preference orders for each # OK, now that we know what the default preference orders for each
...@@ -628,7 +641,7 @@ if "BROWSER" in os.environ: ...@@ -628,7 +641,7 @@ if "BROWSER" in os.environ:
if cmdline != '': if cmdline != '':
cmd = _synthesize(cmdline, -1) cmd = _synthesize(cmdline, -1)
if cmd[1] is None: 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 cmdline = None # to make del work if _userchoices was empty
del cmdline del cmdline
del _userchoices del _userchoices
......
...@@ -1458,6 +1458,7 @@ Quentin Stafford-Fraser ...@@ -1458,6 +1458,7 @@ Quentin Stafford-Fraser
Frank Stajano Frank Stajano
Joel Stanley Joel Stanley
Anthony Starks Anthony Starks
David Steele
Oliver Steele Oliver Steele
Greg Stein Greg Stein
Marek Stepniowski Marek Stepniowski
......
...@@ -433,6 +433,11 @@ Library ...@@ -433,6 +433,11 @@ Library
- Issue #23262: The webbrowser module now supports Firefox 36+ and derived - Issue #23262: The webbrowser module now supports Firefox 36+ and derived
browsers. Based on patch by Oleg Broytman. 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 - Issue #27939: Fixed bugs in tkinter.ttk.LabeledScale and tkinter.Scale caused
by representing the scale as float value internally in Tk. tkinter.IntVar by representing the scale as float value internally in Tk. tkinter.IntVar
now works if float value is set to underlying Tk variable. now works if float value is set to underlying Tk variable.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment