Kaydet (Commit) a7cba27a authored tarafından Serhiy Storchaka's avatar Serhiy Storchaka Kaydeden (comit) GitHub

bpo-29645: Speed up importing the webbrowser module. (#484)

üst 8606e952
...@@ -266,5 +266,30 @@ class BrowserRegistrationTest(unittest.TestCase): ...@@ -266,5 +266,30 @@ class BrowserRegistrationTest(unittest.TestCase):
self._check_registration(preferred=True) self._check_registration(preferred=True)
class ImportTest(unittest.TestCase):
def test_register(self):
webbrowser = support.import_fresh_module('webbrowser')
self.assertIsNone(webbrowser._tryorder)
self.assertFalse(webbrowser._browsers)
class ExampleBrowser:
pass
webbrowser.register('Example1', ExampleBrowser)
self.assertTrue(webbrowser._tryorder)
self.assertEqual(webbrowser._tryorder[-1], 'Example1')
self.assertTrue(webbrowser._browsers)
self.assertIn('example1', webbrowser._browsers)
self.assertEqual(webbrowser._browsers['example1'], [ExampleBrowser, None])
def test_get(self):
webbrowser = support.import_fresh_module('webbrowser')
self.assertIsNone(webbrowser._tryorder)
self.assertFalse(webbrowser._browsers)
with self.assertRaises(webbrowser.Error):
webbrowser.get('fakebrowser')
self.assertIsNotNone(webbrowser._tryorder)
if __name__=='__main__': if __name__=='__main__':
unittest.main() unittest.main()
...@@ -7,30 +7,39 @@ import shlex ...@@ -7,30 +7,39 @@ import shlex
import shutil import shutil
import sys import sys
import subprocess import subprocess
import threading
__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
class Error(Exception): class Error(Exception):
pass pass
_lock = threading.RLock()
_browsers = {} # Dictionary of available browser controllers _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 _os_preferred_browser = None # The preferred browser
def register(name, klass, instance=None, *, preferred=False): def register(name, klass, instance=None, *, preferred=False):
"""Register a browser connector.""" """Register a browser connector."""
_browsers[name.lower()] = [klass, instance] with _lock:
if _tryorder is None:
# Preferred browsers go to the front of the list. register_standard_browsers()
# Need to match to the default browser returned by xdg-settings, which _browsers[name.lower()] = [klass, instance]
# may be of the form e.g. "firefox.desktop".
if preferred or (_os_preferred_browser and name in _os_preferred_browser): # Preferred browsers go to the front of the list.
_tryorder.insert(0, name) # Need to match to the default browser returned by xdg-settings, which
else: # may be of the form e.g. "firefox.desktop".
_tryorder.append(name) if preferred or (_os_preferred_browser and name in _os_preferred_browser):
_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."""
if _tryorder is None:
with _lock:
if _tryorder is None:
register_standard_browsers()
if using is not None: if using is not None:
alternatives = [using] alternatives = [using]
else: else:
...@@ -60,6 +69,10 @@ def get(using=None): ...@@ -60,6 +69,10 @@ def get(using=None):
# instead of "from webbrowser import *". # instead of "from webbrowser import *".
def open(url, new=0, autoraise=True): def open(url, new=0, autoraise=True):
if _tryorder is None:
with _lock:
if _tryorder is None:
register_standard_browsers()
for name in _tryorder: for name in _tryorder:
browser = get(name) browser = get(name)
if browser.open(url, new, autoraise): if browser.open(url, new, autoraise):
...@@ -487,34 +500,76 @@ def register_X_browsers(): ...@@ -487,34 +500,76 @@ def register_X_browsers():
if shutil.which("grail"): if shutil.which("grail"):
register("grail", Grail, None) register("grail", Grail, None)
# Prefer X browsers if present def register_standard_browsers():
if os.environ.get("DISPLAY"): global _tryorder
try: _tryorder = []
cmd = "xdg-settings get default-web-browser".split()
raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) if sys.platform == 'darwin':
result = raw_result.decode().strip() register("MacOSX", None, MacOSXOSAScript('default'))
except (FileNotFoundError, subprocess.CalledProcessError): register("chrome", None, MacOSXOSAScript('chrome'))
pass 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: else:
_os_preferred_browser = result # Prefer X browsers if present
if os.environ.get("DISPLAY"):
register_X_browsers() try:
cmd = "xdg-settings get default-web-browser".split()
# Also try console browsers raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
if os.environ.get("TERM"): result = raw_result.decode().strip()
if shutil.which("www-browser"): except (FileNotFoundError, subprocess.CalledProcessError):
register("www-browser", None, GenericBrowser("www-browser")) pass
# The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/> else:
if shutil.which("links"): global _os_preferred_browser
register("links", None, GenericBrowser("links")) _os_preferred_browser = result
if shutil.which("elinks"):
register("elinks", None, Elinks("elinks")) register_X_browsers()
# The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
if shutil.which("lynx"): # Also try console browsers
register("lynx", None, GenericBrowser("lynx")) if os.environ.get("TERM"):
# The w3m browser <http://w3m.sourceforge.net/> if shutil.which("www-browser"):
if shutil.which("w3m"): register("www-browser", None, GenericBrowser("www-browser"))
register("w3m", None, GenericBrowser("w3m")) # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
if shutil.which("links"):
register("links", None, GenericBrowser("links"))
if shutil.which("elinks"):
register("elinks", None, Elinks("elinks"))
# The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
if shutil.which("lynx"):
register("lynx", None, GenericBrowser("lynx"))
# The w3m browser <http://w3m.sourceforge.net/>
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 # Platform support for Windows
...@@ -532,20 +587,6 @@ if sys.platform[:3] == "win": ...@@ -532,20 +587,6 @@ if sys.platform[:3] == "win":
else: else:
return True 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 # Platform support for MacOS
# #
...@@ -622,34 +663,6 @@ if sys.platform == 'darwin': ...@@ -622,34 +663,6 @@ if sys.platform == 'darwin':
return not rc 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(): def main():
import getopt import getopt
usage = """Usage: %s [-n | -t] url usage = """Usage: %s [-n | -t] url
......
...@@ -270,6 +270,9 @@ Extension Modules ...@@ -270,6 +270,9 @@ Extension Modules
Library Library
------- -------
- bpo-29645: Speed up importing the webbrowser module. webbrowser.register()
is now thread-safe.
- bpo-28231: The zipfile module now accepts path-like objects for external - bpo-28231: The zipfile module now accepts path-like objects for external
paths. paths.
......
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