Skip to content

gh-128540: lookup default webbrowser on Windows #130538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Lib/test/test_webbrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,44 @@ def test_open_new_tab(self):
self._test('open_new_tab')


@unittest.skipUnless(sys.platform[:3] == "win", "Windows test")
class WindowsDefaultTest(unittest.TestCase):
def setUp(self):
support.patch(self, os, "startfile", mock.Mock())
self.browser = webbrowser.WindowsDefault()
support.patch(self, self.browser, "_open_default_browser", mock.Mock())

def test_default(self):
browser = webbrowser.get()
self.assertIsInstance(browser, webbrowser.WindowsDefault)

def test_open_startfile(self):
url = "https://python.org"
self.browser.open(url)
self.browser._open_default_browser.assert_not_called()
os.startfile.assert_called_with(url)

def test_open_browser_lookup(self):
url = "file://python.org"
self.browser.open(url)
self.browser._open_default_browser.assert_called_with(url)
os.startfile.assert_not_called()

def test_open_browser_lookup_fails(self):
url = "file://python.org"
self.browser._open_default_browser.return_value = False
self.browser.open(url)
self.browser._open_default_browser.assert_called_with(url)
os.startfile.assert_called_with(url)

def test_open_browser_lookup_error(self):
url = "file://python.org"
self.browser._open_default_browser.side_effect = OSError('registry failed...')
self.browser.open(url)
self.browser._open_default_browser.assert_called_with(url)
os.startfile.assert_called_with(url)


class BrowserRegistrationTest(unittest.TestCase):

def setUp(self):
Expand Down
53 changes: 53 additions & 0 deletions Lib/webbrowser.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,61 @@ def register_standard_browsers():

if sys.platform[:3] == "win":
class WindowsDefault(BaseBrowser):
def _open_default_browser(self, url):
"""Open a URL with the default browser

launches the web browser no matter what `url` is, unlike startfile.

Raises OSError if registry lookups fail.
Returns False if URL not opened.
"""
try:
import winreg
except ImportError:
return False
# lookup progId for https URLs
# e.g. 'FirefoxURL-abc123'
with winreg.OpenKey(
winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice",
) as key:
browser_id = winreg.QueryValueEx(key, "ProgId")[0]
# lookup launch command-line
# e.g. '"C:\\Program Files\\Mozilla Firefox\\firefox.exe" -osint -url "%1"'
with winreg.OpenKey(
winreg.HKEY_CLASSES_ROOT,
rf"{browser_id}\shell\open\command",
) as key:
browser_cmd = winreg.QueryValueEx(key, "")[0]

# build command-line
if "%1" not in browser_cmd:
# Command is missing '%1' placeholder,
# so we don't know how to build the command to open a file
# would append be safe in this case?
return False

# the rest copied from BackgroundBrowser
cmdline = [arg.replace("%1", url) for arg in shlex.split(browser_cmd)]
try:
p = subprocess.Popen(cmdline)
return p.poll() is None
except OSError:
return False

def open(self, url, new=0, autoraise=True):
sys.audit("webbrowser.open", url)
proto, _sep, _rest = url.partition(":")
if _sep and proto.lower() not in {"http", "https"}:
# need to lookup browser if it's not a web URL
try:
opened = self._open_default_browser(url)
except OSError:
# failed to lookup registry items
opened = False
if opened:
return opened

try:
os.startfile(url)
except OSError:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Ensure web browser is launched by :func:`webbrowser.open` on Windows, even
for ``file://`` URLs.
Loading