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:
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
......
......@@ -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
......
......@@ -1458,6 +1458,7 @@ Quentin Stafford-Fraser
Frank Stajano
Joel Stanley
Anthony Starks
David Steele
Oliver Steele
Greg Stein
Marek Stepniowski
......
......@@ -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.
......
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