https://github.com/python/cpython/commit/96492785b202a92af1b71f8c011ea839ca3ebb07 commit: 96492785b202a92af1b71f8c011ea839ca3ebb07 branch: main author: Min RK <benjami...@gmail.com> committer: ned-deily <n...@python.org> date: 2025-03-14T02:51:15-04:00 summary:
gh-128540: lookup default webbrowser on macOS (#130535) Ensure web browser is launched by `webbrowser.open` on macOS, even for `file://` URLs. files: A Misc/NEWS.d/next/macOS/2025-02-25-10-25-27.gh-issue-128540.QDz3OL.rst M Lib/test/test_webbrowser.py M Lib/webbrowser.py diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py index 4fcbc5c2e59ea3..870ddd7349f494 100644 --- a/Lib/test/test_webbrowser.py +++ b/Lib/test/test_webbrowser.py @@ -1,3 +1,4 @@ +import io import os import re import shlex @@ -5,6 +6,7 @@ import sys import unittest import webbrowser +from functools import partial from test import support from test.support import import_helper from test.support import is_apple_mobile @@ -301,6 +303,69 @@ def test_open_new_tab(self): self._test('open_new_tab') +class MockPopenPipe: + def __init__(self, cmd, mode): + self.cmd = cmd + self.mode = mode + self.pipe = io.StringIO() + self._closed = False + + def write(self, buf): + self.pipe.write(buf) + + def close(self): + self._closed = True + return None + + +@unittest.skipUnless(sys.platform == "darwin", "macOS specific test") +@requires_subprocess() +class MacOSXOSAScriptTest(unittest.TestCase): + def setUp(self): + support.patch(self, os, "popen", self.mock_popen) + self.browser = webbrowser.MacOSXOSAScript("default") + + def mock_popen(self, cmd, mode): + self.popen_pipe = MockPopenPipe(cmd, mode) + return self.popen_pipe + + def test_default(self): + browser = webbrowser.get() + assert isinstance(browser, webbrowser.MacOSXOSAScript) + self.assertEqual(browser.name, "default") + + def test_default_open(self): + url = "https://python.org" + self.browser.open(url) + self.assertTrue(self.popen_pipe._closed) + self.assertEqual(self.popen_pipe.cmd, "osascript") + script = self.popen_pipe.pipe.getvalue() + self.assertEqual(script.strip(), f'open location "{url}"') + + def test_url_quote(self): + self.browser.open('https://python.org/"quote"') + script = self.popen_pipe.pipe.getvalue() + self.assertEqual( + script.strip(), 'open location "https://python.org/%22quote%22"' + ) + + def test_default_browser_lookup(self): + url = "file:///tmp/some-file.html" + self.browser.open(url) + script = self.popen_pipe.pipe.getvalue() + # doesn't actually test the browser lookup works, + # just that the branch is taken + self.assertIn("URLForApplicationToOpenURL", script) + self.assertIn(f'open location "{url}"', script) + + def test_explicit_browser(self): + browser = webbrowser.MacOSXOSAScript("safari") + browser.open("https://python.org") + script = self.popen_pipe.pipe.getvalue() + self.assertIn('tell application "safari"', script) + self.assertIn('open location "https://python.org"', script) + + class BrowserRegistrationTest(unittest.TestCase): def setUp(self): diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py index d2efc72113a917..232d3c3a9c5938 100644 --- a/Lib/webbrowser.py +++ b/Lib/webbrowser.py @@ -597,7 +597,32 @@ def open(self, url, new=0, autoraise=True): sys.audit("webbrowser.open", url) url = url.replace('"', '%22') if self.name == 'default': - script = f'open location "{url}"' # opens in default browser + proto, _sep, _rest = url.partition(":") + if _sep and proto.lower() in {"http", "https"}: + # default web URL, don't need to lookup browser + script = f'open location "{url}"' + else: + # if not a web URL, need to lookup default browser to ensure a browser is launched + # this should always work, but is overkill to lookup http handler + # before launching http + script = f""" + use framework "AppKit" + use AppleScript version "2.4" + use scripting additions + + property NSWorkspace : a reference to current application's NSWorkspace + property NSURL : a reference to current application's NSURL + + set http_url to NSURL's URLWithString:"https://python.org" + set browser_url to (NSWorkspace's sharedWorkspace)'s ¬ + URLForApplicationToOpenURL:http_url + set app_path to browser_url's relativePath as text -- NSURL to absolute path '/Applications/Safari.app' + + tell application app_path + activate + open location "{url}" + end tell + """ else: script = f''' tell application "{self.name}" diff --git a/Misc/NEWS.d/next/macOS/2025-02-25-10-25-27.gh-issue-128540.QDz3OL.rst b/Misc/NEWS.d/next/macOS/2025-02-25-10-25-27.gh-issue-128540.QDz3OL.rst new file mode 100644 index 00000000000000..096ace7231afb5 --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2025-02-25-10-25-27.gh-issue-128540.QDz3OL.rst @@ -0,0 +1,2 @@ +Ensure web browser is launched by :func:`webbrowser.open` on macOS, even for +``file://`` URLs. _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com