diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index fb6c83b..2a5729b 100755 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -7,30 +7,39 @@ import shlex import shutil import sys import subprocess +import threading __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] class Error(Exception): pass +_lock = threading.RLock() _browsers = {} # Dictionary of available browser controllers -_tryorder = [] # Preference order of available browsers +_tryorder = None # 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] - - # 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) + with _lock: + if _tryorder is None: + register_standard_browsers() + _browsers[name.lower()] = [klass, instance] + + # 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.""" + if _tryorder is None: + with _lock: + if _tryorder is None: + register_standard_browsers() if using is not None: alternatives = [using] else: @@ -60,6 +69,10 @@ def get(using=None): # instead of "from webbrowser import *". def open(url, new=0, autoraise=True): + if _tryorder is None: + with _lock: + if _tryorder is None: + register_standard_browsers() for name in _tryorder: browser = get(name) if browser.open(url, new, autoraise): @@ -487,34 +500,76 @@ def register_X_browsers(): if shutil.which("grail"): register("grail", Grail, None) -# Prefer X browsers if present -if os.environ.get("DISPLAY"): - try: - cmd = "xdg-settings get default-web-browser".split() - raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) - result = raw_result.decode().strip() - except (FileNotFoundError, subprocess.CalledProcessError): - pass +def register_standard_browsers(): + global _tryorder + _tryorder = [] + + if sys.platform == 'darwin': + register("MacOSX", None, MacOSXOSAScript('default')) + register("chrome", None, MacOSXOSAScript('chrome')) + register("firefox", None, MacOSXOSAScript('firefox')) + register("safari", None, MacOSXOSAScript('safari')) + # OS X can use below Unix support (but we prefer using the OS X + # specific stuff) + + if sys.platform[:3] == "win": + # First try to use the default Windows browser + register("windows-default", WindowsDefault) + + # Detect some common Windows browsers, fallback to IE + iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"), + "Internet Explorer\\IEXPLORE.EXE") + for browser in ("firefox", "firebird", "seamonkey", "mozilla", + "netscape", "opera", iexplore): + if shutil.which(browser): + register(browser, None, BackgroundBrowser(browser)) else: - _os_preferred_browser = result - - register_X_browsers() - -# Also try console browsers -if os.environ.get("TERM"): - if shutil.which("www-browser"): - register("www-browser", None, GenericBrowser("www-browser")) - # The Links/elinks browsers - if shutil.which("links"): - register("links", None, GenericBrowser("links")) - if shutil.which("elinks"): - register("elinks", None, Elinks("elinks")) - # The Lynx browser , - if shutil.which("lynx"): - register("lynx", None, GenericBrowser("lynx")) - # The w3m browser - if shutil.which("w3m"): - register("w3m", None, GenericBrowser("w3m")) + # Prefer X browsers if present + if os.environ.get("DISPLAY"): + try: + cmd = "xdg-settings get default-web-browser".split() + raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + result = raw_result.decode().strip() + except (FileNotFoundError, subprocess.CalledProcessError): + pass + else: + global _os_preferred_browser + _os_preferred_browser = result + + register_X_browsers() + + # Also try console browsers + if os.environ.get("TERM"): + if shutil.which("www-browser"): + register("www-browser", None, GenericBrowser("www-browser")) + # The Links/elinks browsers + if shutil.which("links"): + register("links", None, GenericBrowser("links")) + if shutil.which("elinks"): + register("elinks", None, Elinks("elinks")) + # The Lynx browser , + if shutil.which("lynx"): + register("lynx", None, GenericBrowser("lynx")) + # The w3m browser + if shutil.which("w3m"): + register("w3m", None, GenericBrowser("w3m")) + + # OK, now that we know what the default preference orders for each + # platform are, allow user to override them with the BROWSER variable. + if "BROWSER" in os.environ: + userchoices = os.environ["BROWSER"].split(os.pathsep) + userchoices.reverse() + + # Treat choices in same way as if passed into get() but do register + # and prepend to _tryorder + for cmdline in userchoices: + if cmdline != '': + cmd = _synthesize(cmdline, -1) + if cmd[1] is None: + register(cmdline, None, GenericBrowser(cmdline), preferred=True) + + # what to do if _tryorder is now empty? + # # Platform support for Windows @@ -532,20 +587,6 @@ if sys.platform[:3] == "win": else: return True - _tryorder = [] - _browsers = {} - - # First try to use the default Windows browser - register("windows-default", WindowsDefault) - - # Detect some common Windows browsers, fallback to IE - iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"), - "Internet Explorer\\IEXPLORE.EXE") - for browser in ("firefox", "firebird", "seamonkey", "mozilla", - "netscape", "opera", iexplore): - if shutil.which(browser): - register(browser, None, BackgroundBrowser(browser)) - # # Platform support for MacOS # @@ -622,34 +663,6 @@ if sys.platform == 'darwin': return not rc - # 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'), 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 -# platform are, allow user to override them with the BROWSER variable. -if "BROWSER" in os.environ: - _userchoices = os.environ["BROWSER"].split(os.pathsep) - _userchoices.reverse() - - # Treat choices in same way as if passed into get() but do register - # and prepend to _tryorder - for cmdline in _userchoices: - if cmdline != '': - cmd = _synthesize(cmdline, -1) - if cmd[1] is None: - register(cmdline, None, GenericBrowser(cmdline), preferred=True) - cmdline = None # to make del work if _userchoices was empty - del cmdline - del _userchoices - -# what to do if _tryorder is now empty? - - def main(): import getopt usage = """Usage: %s [-n | -t] url