From 30a06fdb13a193b01c8b53353cdd325800ef25af Mon Sep 17 00:00:00 2001 From: David Steele Date: Sun, 12 Feb 2017 15:17:47 -0500 Subject: [PATCH 1/2] webbrowser.register: Add 'preferred' argument Replace the existing undocumented tri-state 'try_order' parameter with the 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. 'preferred' is added to the documentation. --- Doc/library/webbrowser.rst | 10 ++++++---- Lib/webbrowser.py | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Doc/library/webbrowser.rst b/Doc/library/webbrowser.rst index 85d36367221295..ee501e80d9ed61 100644 --- a/Doc/library/webbrowser.rst +++ b/Doc/library/webbrowser.rst @@ -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 diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index 6f43b7f1265d1a..8f54ddc4ab9d7f 100755 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -16,13 +16,13 @@ class Error(Exception): _browsers = {} # Dictionary of available browser controllers _tryorder = [] # Preference order of available browsers -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: + if preferred: _tryorder.insert(0, name) + else: + _tryorder.append(name) def get(using=None): """Return a browser launcher instance appropriate for the environment.""" @@ -610,10 +610,10 @@ def open(self, url, new=0, autoraise=True): # 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 +628,7 @@ def open(self, url, new=0, autoraise=True): 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 From 871455f7591527b37889d6e359683608a0f4c33d Mon Sep 17 00:00:00 2001 From: David Steele Date: Sun, 12 Feb 2017 15:32:28 -0500 Subject: [PATCH 2/2] bpo-24241: xdg-settings for preferred X browser The traditional first entry in the tryorder queue is xdg-open, which doesn't support new window. Make the default browser first in the try list, to properly support these options. --- Lib/webbrowser.py | 19 ++++++++++++++++--- Misc/ACKS | 1 + Misc/NEWS | 5 +++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index 8f54ddc4ab9d7f..a9eac69650546f 100755 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -13,13 +13,18 @@ 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, *, preferred=False): """Register a browser connector.""" _browsers[name.lower()] = [klass, instance] - if preferred: + + # 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) @@ -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 diff --git a/Misc/ACKS b/Misc/ACKS index 5ab6411688c7d1..e63a061098e8e0 100644 --- 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 diff --git a/Misc/NEWS b/Misc/NEWS index faae5f7e2a7196..87348ac73cc65d 100644 --- 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.