mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
update webbrowser and test_webbrowser to 3.13.3
Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
committed by
Jeong, YunWon
parent
82a62382d0
commit
320d74527f
250
Lib/test/test_webbrowser.py
vendored
250
Lib/test/test_webbrowser.py
vendored
@@ -1,15 +1,22 @@
|
||||
import webbrowser
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
from unittest import mock
|
||||
import sys
|
||||
import unittest
|
||||
import webbrowser
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
from test.support import is_apple_mobile
|
||||
from test.support import os_helper
|
||||
from test.support import requires_subprocess
|
||||
from test.support import threading_helper
|
||||
from unittest import mock
|
||||
|
||||
# The webbrowser module uses threading locks
|
||||
threading_helper.requires_working_threading(module=True)
|
||||
|
||||
URL = 'http://www.example.com'
|
||||
URL = 'https://www.example.com'
|
||||
CMD_NAME = 'test'
|
||||
|
||||
|
||||
@@ -22,6 +29,7 @@ class PopenMock(mock.MagicMock):
|
||||
return 0
|
||||
|
||||
|
||||
@requires_subprocess()
|
||||
class CommandTestMixin:
|
||||
|
||||
def _test(self, meth, *, args=[URL], kw={}, options, arguments):
|
||||
@@ -92,6 +100,40 @@ class ChromeCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
options=[],
|
||||
arguments=[URL])
|
||||
|
||||
def test_open_bad_new_parameter(self):
|
||||
with self.assertRaisesRegex(webbrowser.Error,
|
||||
re.escape("Bad 'new' parameter to open(); "
|
||||
"expected 0, 1, or 2, got 999")):
|
||||
self._test('open',
|
||||
options=[],
|
||||
arguments=[URL],
|
||||
kw=dict(new=999))
|
||||
|
||||
|
||||
class EdgeCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
|
||||
browser_class = webbrowser.Edge
|
||||
|
||||
def test_open(self):
|
||||
self._test('open',
|
||||
options=[],
|
||||
arguments=[URL])
|
||||
|
||||
def test_open_with_autoraise_false(self):
|
||||
self._test('open', kw=dict(autoraise=False),
|
||||
options=[],
|
||||
arguments=[URL])
|
||||
|
||||
def test_open_new(self):
|
||||
self._test('open_new',
|
||||
options=['--new-window'],
|
||||
arguments=[URL])
|
||||
|
||||
def test_open_new_tab(self):
|
||||
self._test('open_new_tab',
|
||||
options=[],
|
||||
arguments=[URL])
|
||||
|
||||
|
||||
class MozillaCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
|
||||
@@ -118,34 +160,9 @@ class MozillaCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
arguments=['-new-tab', URL])
|
||||
|
||||
|
||||
class NetscapeCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
class EpiphanyCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
|
||||
browser_class = webbrowser.Netscape
|
||||
|
||||
def test_open(self):
|
||||
self._test('open',
|
||||
options=['-raise', '-remote'],
|
||||
arguments=['openURL({})'.format(URL)])
|
||||
|
||||
def test_open_with_autoraise_false(self):
|
||||
self._test('open', kw=dict(autoraise=False),
|
||||
options=['-noraise', '-remote'],
|
||||
arguments=['openURL({})'.format(URL)])
|
||||
|
||||
def test_open_new(self):
|
||||
self._test('open_new',
|
||||
options=['-raise', '-remote'],
|
||||
arguments=['openURL({},new-window)'.format(URL)])
|
||||
|
||||
def test_open_new_tab(self):
|
||||
self._test('open_new_tab',
|
||||
options=['-raise', '-remote'],
|
||||
arguments=['openURL({},new-tab)'.format(URL)])
|
||||
|
||||
|
||||
class GaleonCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
|
||||
browser_class = webbrowser.Galeon
|
||||
browser_class = webbrowser.Epiphany
|
||||
|
||||
def test_open(self):
|
||||
self._test('open',
|
||||
@@ -199,22 +216,89 @@ class ELinksCommandTest(CommandTestMixin, unittest.TestCase):
|
||||
|
||||
def test_open(self):
|
||||
self._test('open', options=['-remote'],
|
||||
arguments=['openURL({})'.format(URL)])
|
||||
arguments=[f'openURL({URL})'])
|
||||
|
||||
def test_open_with_autoraise_false(self):
|
||||
self._test('open',
|
||||
options=['-remote'],
|
||||
arguments=['openURL({})'.format(URL)])
|
||||
arguments=[f'openURL({URL})'])
|
||||
|
||||
def test_open_new(self):
|
||||
self._test('open_new',
|
||||
options=['-remote'],
|
||||
arguments=['openURL({},new-window)'.format(URL)])
|
||||
arguments=[f'openURL({URL},new-window)'])
|
||||
|
||||
def test_open_new_tab(self):
|
||||
self._test('open_new_tab',
|
||||
options=['-remote'],
|
||||
arguments=['openURL({},new-tab)'.format(URL)])
|
||||
arguments=[f'openURL({URL},new-tab)'])
|
||||
|
||||
|
||||
@unittest.skipUnless(sys.platform == "ios", "Test only applicable to iOS")
|
||||
class IOSBrowserTest(unittest.TestCase):
|
||||
def _obj_ref(self, *args):
|
||||
# Construct a string representation of the arguments that can be used
|
||||
# as a proxy for object instance references
|
||||
return "|".join(str(a) for a in args)
|
||||
|
||||
@unittest.skipIf(getattr(webbrowser, "objc", None) is None,
|
||||
"iOS Webbrowser tests require ctypes")
|
||||
def setUp(self):
|
||||
# Intercept the objc library. Wrap the calls to get the
|
||||
# references to classes and selectors to return strings, and
|
||||
# wrap msgSend to return stringified object references
|
||||
self.orig_objc = webbrowser.objc
|
||||
|
||||
webbrowser.objc = mock.Mock()
|
||||
webbrowser.objc.objc_getClass = lambda cls: f"C#{cls.decode()}"
|
||||
webbrowser.objc.sel_registerName = lambda sel: f"S#{sel.decode()}"
|
||||
webbrowser.objc.objc_msgSend.side_effect = self._obj_ref
|
||||
|
||||
def tearDown(self):
|
||||
webbrowser.objc = self.orig_objc
|
||||
|
||||
def _test(self, meth, **kwargs):
|
||||
# The browser always gets focus, there's no concept of separate browser
|
||||
# windows, and there's no API-level control over creating a new tab.
|
||||
# Therefore, all calls to webbrowser are effectively the same.
|
||||
getattr(webbrowser, meth)(URL, **kwargs)
|
||||
|
||||
# The ObjC String version of the URL is created with UTF-8 encoding
|
||||
url_string_args = [
|
||||
"C#NSString",
|
||||
"S#stringWithCString:encoding:",
|
||||
b'https://www.example.com',
|
||||
4,
|
||||
]
|
||||
# The NSURL version of the URL is created from that string
|
||||
url_obj_args = [
|
||||
"C#NSURL",
|
||||
"S#URLWithString:",
|
||||
self._obj_ref(*url_string_args),
|
||||
]
|
||||
# The openURL call is invoked on the shared application
|
||||
shared_app_args = ["C#UIApplication", "S#sharedApplication"]
|
||||
|
||||
# Verify that the last call is the one that opens the URL.
|
||||
webbrowser.objc.objc_msgSend.assert_called_with(
|
||||
self._obj_ref(*shared_app_args),
|
||||
"S#openURL:options:completionHandler:",
|
||||
self._obj_ref(*url_obj_args),
|
||||
None,
|
||||
None
|
||||
)
|
||||
|
||||
def test_open(self):
|
||||
self._test('open')
|
||||
|
||||
def test_open_with_autoraise_false(self):
|
||||
self._test('open', autoraise=False)
|
||||
|
||||
def test_open_new(self):
|
||||
self._test('open_new')
|
||||
|
||||
def test_open_new_tab(self):
|
||||
self._test('open_new_tab')
|
||||
|
||||
|
||||
class BrowserRegistrationTest(unittest.TestCase):
|
||||
@@ -269,6 +353,16 @@ class BrowserRegistrationTest(unittest.TestCase):
|
||||
def test_register_preferred(self):
|
||||
self._check_registration(preferred=True)
|
||||
|
||||
@unittest.skipUnless(sys.platform == "darwin", "macOS specific test")
|
||||
def test_no_xdg_settings_on_macOS(self):
|
||||
# On macOS webbrowser should not use xdg-settings to
|
||||
# look for X11 based browsers (for those users with
|
||||
# XQuartz installed)
|
||||
with mock.patch("subprocess.check_output") as ck_o:
|
||||
webbrowser.register_standard_browsers()
|
||||
|
||||
ck_o.assert_not_called()
|
||||
|
||||
|
||||
class ImportTest(unittest.TestCase):
|
||||
def test_register(self):
|
||||
@@ -294,29 +388,38 @@ class ImportTest(unittest.TestCase):
|
||||
webbrowser.get('fakebrowser')
|
||||
self.assertIsNotNone(webbrowser._tryorder)
|
||||
|
||||
@unittest.skipIf(" " in sys.executable, "test assumes no space in path (GH-114452)")
|
||||
def test_synthesize(self):
|
||||
webbrowser = import_helper.import_fresh_module('webbrowser')
|
||||
name = os.path.basename(sys.executable).lower()
|
||||
webbrowser.register(name, None, webbrowser.GenericBrowser(name))
|
||||
webbrowser.get(sys.executable)
|
||||
|
||||
@unittest.skipIf(
|
||||
is_apple_mobile,
|
||||
"Apple mobile doesn't allow modifying browser with environment"
|
||||
)
|
||||
def test_environment(self):
|
||||
webbrowser = import_helper.import_fresh_module('webbrowser')
|
||||
try:
|
||||
browser = webbrowser.get().name
|
||||
except (webbrowser.Error, AttributeError) as err:
|
||||
except webbrowser.Error as err:
|
||||
self.skipTest(str(err))
|
||||
with os_helper.EnvironmentVarGuard() as env:
|
||||
env["BROWSER"] = browser
|
||||
webbrowser = import_helper.import_fresh_module('webbrowser')
|
||||
webbrowser.get()
|
||||
|
||||
@unittest.skipIf(
|
||||
is_apple_mobile,
|
||||
"Apple mobile doesn't allow modifying browser with environment"
|
||||
)
|
||||
def test_environment_preferred(self):
|
||||
webbrowser = import_helper.import_fresh_module('webbrowser')
|
||||
try:
|
||||
webbrowser.get()
|
||||
least_preferred_browser = webbrowser.get(webbrowser._tryorder[-1]).name
|
||||
except (webbrowser.Error, AttributeError, IndexError) as err:
|
||||
except (webbrowser.Error, IndexError) as err:
|
||||
self.skipTest(str(err))
|
||||
|
||||
with os_helper.EnvironmentVarGuard() as env:
|
||||
@@ -330,5 +433,74 @@ class ImportTest(unittest.TestCase):
|
||||
self.assertEqual(webbrowser.get().name, sys.executable)
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
class CliTest(unittest.TestCase):
|
||||
def test_parse_args(self):
|
||||
for command, url, new_win in [
|
||||
# No optional arguments
|
||||
("https://example.com", "https://example.com", 0),
|
||||
# Each optional argument
|
||||
("https://example.com -n", "https://example.com", 1),
|
||||
("-n https://example.com", "https://example.com", 1),
|
||||
("https://example.com -t", "https://example.com", 2),
|
||||
("-t https://example.com", "https://example.com", 2),
|
||||
# Long form
|
||||
("https://example.com --new-window", "https://example.com", 1),
|
||||
("--new-window https://example.com", "https://example.com", 1),
|
||||
("https://example.com --new-tab", "https://example.com", 2),
|
||||
("--new-tab https://example.com", "https://example.com", 2),
|
||||
]:
|
||||
args = webbrowser.parse_args(shlex.split(command))
|
||||
|
||||
self.assertEqual(args.url, url)
|
||||
self.assertEqual(args.new_win, new_win)
|
||||
|
||||
def test_parse_args_error(self):
|
||||
for command in [
|
||||
# Arguments must not both be given
|
||||
"https://example.com -n -t",
|
||||
"https://example.com --new-window --new-tab",
|
||||
"https://example.com -n --new-tab",
|
||||
"https://example.com --new-window -t",
|
||||
]:
|
||||
with support.captured_stderr() as stderr:
|
||||
with self.assertRaises(SystemExit):
|
||||
webbrowser.parse_args(shlex.split(command))
|
||||
self.assertIn(
|
||||
'error: argument -t/--new-tab: not allowed with argument -n/--new-window',
|
||||
stderr.getvalue(),
|
||||
)
|
||||
|
||||
# Ensure ambiguous shortening fails
|
||||
with support.captured_stderr() as stderr:
|
||||
with self.assertRaises(SystemExit):
|
||||
webbrowser.parse_args(shlex.split("https://example.com --new"))
|
||||
self.assertIn(
|
||||
'error: ambiguous option: --new could match --new-window, --new-tab',
|
||||
stderr.getvalue()
|
||||
)
|
||||
|
||||
def test_main(self):
|
||||
for command, expected_url, expected_new_win in [
|
||||
# No optional arguments
|
||||
("https://example.com", "https://example.com", 0),
|
||||
# Each optional argument
|
||||
("https://example.com -n", "https://example.com", 1),
|
||||
("-n https://example.com", "https://example.com", 1),
|
||||
("https://example.com -t", "https://example.com", 2),
|
||||
("-t https://example.com", "https://example.com", 2),
|
||||
# Long form
|
||||
("https://example.com --new-window", "https://example.com", 1),
|
||||
("--new-window https://example.com", "https://example.com", 1),
|
||||
("https://example.com --new-tab", "https://example.com", 2),
|
||||
("--new-tab https://example.com", "https://example.com", 2),
|
||||
]:
|
||||
with (
|
||||
mock.patch("webbrowser.open", return_value=None) as mock_open,
|
||||
mock.patch("builtins.print", return_value=None),
|
||||
):
|
||||
webbrowser.main(shlex.split(command))
|
||||
mock_open.assert_called_once_with(expected_url, expected_new_win)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
355
Lib/webbrowser.py
vendored
355
Lib/webbrowser.py
vendored
@@ -11,14 +11,17 @@ 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 = 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."""
|
||||
with _lock:
|
||||
@@ -29,11 +32,12 @@ def register(name, klass, instance=None, *, preferred=False):
|
||||
# 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):
|
||||
if preferred or (_os_preferred_browser and f'{name}.desktop' == _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:
|
||||
@@ -64,6 +68,7 @@ def get(using=None):
|
||||
return command[0]()
|
||||
raise Error("could not locate runnable browser")
|
||||
|
||||
|
||||
# Please note: the following definition hides a builtin function.
|
||||
# It is recommended one does "import webbrowser" and uses webbrowser.open(url)
|
||||
# instead of "from webbrowser import *".
|
||||
@@ -76,6 +81,9 @@ def open(url, new=0, autoraise=True):
|
||||
- 1: a new browser window.
|
||||
- 2: a new browser page ("tab").
|
||||
If possible, autoraise raises the window (the default) or not.
|
||||
|
||||
If opening the browser succeeds, return True.
|
||||
If there is a problem, return False.
|
||||
"""
|
||||
if _tryorder is None:
|
||||
with _lock:
|
||||
@@ -87,6 +95,7 @@ def open(url, new=0, autoraise=True):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def open_new(url):
|
||||
"""Open url in a new window of the default browser.
|
||||
|
||||
@@ -94,6 +103,7 @@ def open_new(url):
|
||||
"""
|
||||
return open(url, 1)
|
||||
|
||||
|
||||
def open_new_tab(url):
|
||||
"""Open url in a new page ("tab") of the default browser.
|
||||
|
||||
@@ -136,7 +146,7 @@ def _synthesize(browser, *, preferred=False):
|
||||
|
||||
# General parent classes
|
||||
|
||||
class BaseBrowser(object):
|
||||
class BaseBrowser:
|
||||
"""Parent class for all browsers. Do not use directly."""
|
||||
|
||||
args = ['%s']
|
||||
@@ -197,7 +207,7 @@ class BackgroundBrowser(GenericBrowser):
|
||||
else:
|
||||
p = subprocess.Popen(cmdline, close_fds=True,
|
||||
start_new_session=True)
|
||||
return (p.poll() is None)
|
||||
return p.poll() is None
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
@@ -225,7 +235,8 @@ class UnixBrowser(BaseBrowser):
|
||||
# use autoraise argument only for remote invocation
|
||||
autoraise = int(autoraise)
|
||||
opt = self.raise_opts[autoraise]
|
||||
if opt: raise_opt = [opt]
|
||||
if opt:
|
||||
raise_opt = [opt]
|
||||
|
||||
cmdline = [self.name] + raise_opt + args
|
||||
|
||||
@@ -266,8 +277,8 @@ class UnixBrowser(BaseBrowser):
|
||||
else:
|
||||
action = self.remote_action_newtab
|
||||
else:
|
||||
raise Error("Bad 'new' parameter to open(); " +
|
||||
"expected 0, 1, or 2, got %s" % new)
|
||||
raise Error("Bad 'new' parameter to open(); "
|
||||
f"expected 0, 1, or 2, got {new}")
|
||||
|
||||
args = [arg.replace("%s", url).replace("%action", action)
|
||||
for arg in self.remote_args]
|
||||
@@ -291,19 +302,8 @@ class Mozilla(UnixBrowser):
|
||||
background = True
|
||||
|
||||
|
||||
class Netscape(UnixBrowser):
|
||||
"""Launcher class for Netscape browser."""
|
||||
|
||||
raise_opts = ["-noraise", "-raise"]
|
||||
remote_args = ['-remote', 'openURL(%s%action)']
|
||||
remote_action = ""
|
||||
remote_action_newwin = ",new-window"
|
||||
remote_action_newtab = ",new-tab"
|
||||
background = True
|
||||
|
||||
|
||||
class Galeon(UnixBrowser):
|
||||
"""Launcher class for Galeon/Epiphany browsers."""
|
||||
class Epiphany(UnixBrowser):
|
||||
"""Launcher class for Epiphany browser."""
|
||||
|
||||
raise_opts = ["-noraise", ""]
|
||||
remote_args = ['%action', '%s']
|
||||
@@ -313,7 +313,7 @@ class Galeon(UnixBrowser):
|
||||
|
||||
|
||||
class Chrome(UnixBrowser):
|
||||
"Launcher class for Google Chrome browser."
|
||||
"""Launcher class for Google Chrome browser."""
|
||||
|
||||
remote_args = ['%action', '%s']
|
||||
remote_action = ""
|
||||
@@ -321,11 +321,12 @@ class Chrome(UnixBrowser):
|
||||
remote_action_newtab = ""
|
||||
background = True
|
||||
|
||||
|
||||
Chromium = Chrome
|
||||
|
||||
|
||||
class Opera(UnixBrowser):
|
||||
"Launcher class for Opera browser."
|
||||
"""Launcher class for Opera browser."""
|
||||
|
||||
remote_args = ['%action', '%s']
|
||||
remote_action = ""
|
||||
@@ -335,7 +336,7 @@ class Opera(UnixBrowser):
|
||||
|
||||
|
||||
class Elinks(UnixBrowser):
|
||||
"Launcher class for Elinks browsers."
|
||||
"""Launcher class for Elinks browsers."""
|
||||
|
||||
remote_args = ['-remote', 'openURL(%s%action)']
|
||||
remote_action = ""
|
||||
@@ -398,54 +399,17 @@ class Konqueror(BaseBrowser):
|
||||
except OSError:
|
||||
return False
|
||||
else:
|
||||
return (p.poll() is None)
|
||||
return p.poll() is None
|
||||
|
||||
|
||||
class Grail(BaseBrowser):
|
||||
# There should be a way to maintain a connection to Grail, but the
|
||||
# Grail remote control protocol doesn't really allow that at this
|
||||
# point. It probably never will!
|
||||
def _find_grail_rc(self):
|
||||
import glob
|
||||
import pwd
|
||||
import socket
|
||||
import tempfile
|
||||
tempdir = os.path.join(tempfile.gettempdir(),
|
||||
".grail-unix")
|
||||
user = pwd.getpwuid(os.getuid())[0]
|
||||
filename = os.path.join(glob.escape(tempdir), glob.escape(user) + "-*")
|
||||
maybes = glob.glob(filename)
|
||||
if not maybes:
|
||||
return None
|
||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
for fn in maybes:
|
||||
# need to PING each one until we find one that's live
|
||||
try:
|
||||
s.connect(fn)
|
||||
except OSError:
|
||||
# no good; attempt to clean it out, but don't fail:
|
||||
try:
|
||||
os.unlink(fn)
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
return s
|
||||
class Edge(UnixBrowser):
|
||||
"""Launcher class for Microsoft Edge browser."""
|
||||
|
||||
def _remote(self, action):
|
||||
s = self._find_grail_rc()
|
||||
if not s:
|
||||
return 0
|
||||
s.send(action)
|
||||
s.close()
|
||||
return 1
|
||||
|
||||
def open(self, url, new=0, autoraise=True):
|
||||
sys.audit("webbrowser.open", url)
|
||||
if new:
|
||||
ok = self._remote("LOADNEW " + url)
|
||||
else:
|
||||
ok = self._remote("LOAD " + url)
|
||||
return ok
|
||||
remote_args = ['%action', '%s']
|
||||
remote_action = ""
|
||||
remote_action_newwin = "--new-window"
|
||||
remote_action_newtab = ""
|
||||
background = True
|
||||
|
||||
|
||||
#
|
||||
@@ -461,47 +425,44 @@ def register_X_browsers():
|
||||
if shutil.which("xdg-open"):
|
||||
register("xdg-open", None, BackgroundBrowser("xdg-open"))
|
||||
|
||||
# Opens an appropriate browser for the URL scheme according to
|
||||
# freedesktop.org settings (GNOME, KDE, XFCE, etc.)
|
||||
if shutil.which("gio"):
|
||||
register("gio", None, BackgroundBrowser(["gio", "open", "--", "%s"]))
|
||||
|
||||
xdg_desktop = os.getenv("XDG_CURRENT_DESKTOP", "").split(":")
|
||||
|
||||
# The default GNOME3 browser
|
||||
if "GNOME_DESKTOP_SESSION_ID" in os.environ and shutil.which("gvfs-open"):
|
||||
if (("GNOME" in xdg_desktop or
|
||||
"GNOME_DESKTOP_SESSION_ID" in os.environ) and
|
||||
shutil.which("gvfs-open")):
|
||||
register("gvfs-open", None, BackgroundBrowser("gvfs-open"))
|
||||
|
||||
# The default GNOME browser
|
||||
if "GNOME_DESKTOP_SESSION_ID" in os.environ and shutil.which("gnome-open"):
|
||||
register("gnome-open", None, BackgroundBrowser("gnome-open"))
|
||||
|
||||
# The default KDE browser
|
||||
if "KDE_FULL_SESSION" in os.environ and shutil.which("kfmclient"):
|
||||
if (("KDE" in xdg_desktop or
|
||||
"KDE_FULL_SESSION" in os.environ) and
|
||||
shutil.which("kfmclient")):
|
||||
register("kfmclient", Konqueror, Konqueror("kfmclient"))
|
||||
|
||||
# Common symbolic link for the default X11 browser
|
||||
if shutil.which("x-www-browser"):
|
||||
register("x-www-browser", None, BackgroundBrowser("x-www-browser"))
|
||||
|
||||
# The Mozilla browsers
|
||||
for browser in ("firefox", "iceweasel", "iceape", "seamonkey"):
|
||||
for browser in ("firefox", "iceweasel", "seamonkey", "mozilla-firefox",
|
||||
"mozilla"):
|
||||
if shutil.which(browser):
|
||||
register(browser, None, Mozilla(browser))
|
||||
|
||||
# The Netscape and old Mozilla browsers
|
||||
for browser in ("mozilla-firefox",
|
||||
"mozilla-firebird", "firebird",
|
||||
"mozilla", "netscape"):
|
||||
if shutil.which(browser):
|
||||
register(browser, None, Netscape(browser))
|
||||
|
||||
# Konqueror/kfm, the KDE browser.
|
||||
if shutil.which("kfm"):
|
||||
register("kfm", Konqueror, Konqueror("kfm"))
|
||||
elif shutil.which("konqueror"):
|
||||
register("konqueror", Konqueror, Konqueror("konqueror"))
|
||||
|
||||
# Gnome's Galeon and Epiphany
|
||||
for browser in ("galeon", "epiphany"):
|
||||
if shutil.which(browser):
|
||||
register(browser, None, Galeon(browser))
|
||||
|
||||
# Skipstone, another Gtk/Mozilla based browser
|
||||
if shutil.which("skipstone"):
|
||||
register("skipstone", None, BackgroundBrowser("skipstone"))
|
||||
# Gnome's Epiphany
|
||||
if shutil.which("epiphany"):
|
||||
register("epiphany", None, Epiphany("epiphany"))
|
||||
|
||||
# Google Chrome/Chromium browsers
|
||||
for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"):
|
||||
@@ -512,13 +473,9 @@ def register_X_browsers():
|
||||
if shutil.which("opera"):
|
||||
register("opera", None, Opera("opera"))
|
||||
|
||||
# Next, Mosaic -- old but still in use.
|
||||
if shutil.which("mosaic"):
|
||||
register("mosaic", None, BackgroundBrowser("mosaic"))
|
||||
if shutil.which("microsoft-edge"):
|
||||
register("microsoft-edge", None, Edge("microsoft-edge"))
|
||||
|
||||
# Grail, the Python browser. Does anybody still use it?
|
||||
if shutil.which("grail"):
|
||||
register("grail", Grail, None)
|
||||
|
||||
def register_standard_browsers():
|
||||
global _tryorder
|
||||
@@ -532,6 +489,9 @@ def register_standard_browsers():
|
||||
# OS X can use below Unix support (but we prefer using the OS X
|
||||
# specific stuff)
|
||||
|
||||
if sys.platform == "ios":
|
||||
register("iosbrowser", None, IOSBrowser(), preferred=True)
|
||||
|
||||
if sys.platform == "serenityos":
|
||||
# SerenityOS webbrowser, simply called "Browser".
|
||||
register("Browser", None, BackgroundBrowser("Browser"))
|
||||
@@ -540,21 +500,33 @@ def register_standard_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):
|
||||
# Detect some common Windows browsers, fallback to Microsoft Edge
|
||||
# location in 64-bit Windows
|
||||
edge64 = os.path.join(os.environ.get("PROGRAMFILES(x86)", "C:\\Program Files (x86)"),
|
||||
"Microsoft\\Edge\\Application\\msedge.exe")
|
||||
# location in 32-bit Windows
|
||||
edge32 = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
|
||||
"Microsoft\\Edge\\Application\\msedge.exe")
|
||||
for browser in ("firefox", "seamonkey", "mozilla", "chrome",
|
||||
"opera", edge64, edge32):
|
||||
if shutil.which(browser):
|
||||
register(browser, None, BackgroundBrowser(browser))
|
||||
if shutil.which("MicrosoftEdge.exe"):
|
||||
register("microsoft-edge", None, Edge("MicrosoftEdge.exe"))
|
||||
else:
|
||||
# Prefer X browsers if present
|
||||
if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"):
|
||||
#
|
||||
# NOTE: Do not check for X11 browser on macOS,
|
||||
# XQuartz installation sets a DISPLAY environment variable and will
|
||||
# autostart when someone tries to access the display. Mac users in
|
||||
# general don't need an X11 browser.
|
||||
if sys.platform != "darwin" and (os.environ.get("DISPLAY") or os.environ.get("WAYLAND_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, PermissionError, NotADirectoryError) :
|
||||
except (FileNotFoundError, subprocess.CalledProcessError,
|
||||
PermissionError, NotADirectoryError):
|
||||
pass
|
||||
else:
|
||||
global _os_preferred_browser
|
||||
@@ -564,14 +536,15 @@ def register_standard_browsers():
|
||||
|
||||
# Also try console browsers
|
||||
if os.environ.get("TERM"):
|
||||
# Common symbolic link for the default text-based browser
|
||||
if shutil.which("www-browser"):
|
||||
register("www-browser", None, GenericBrowser("www-browser"))
|
||||
# The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
|
||||
# The Links/elinks browsers <http://links.twibright.com/>
|
||||
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/>
|
||||
# The Lynx browser <https://lynx.invisible-island.net/>, <http://lynx.browser.org/>
|
||||
if shutil.which("lynx"):
|
||||
register("lynx", None, GenericBrowser("lynx"))
|
||||
# The w3m browser <http://w3m.sourceforge.net/>
|
||||
@@ -613,72 +586,26 @@ if sys.platform[:3] == "win":
|
||||
return True
|
||||
|
||||
#
|
||||
# Platform support for MacOS
|
||||
# Platform support for macOS
|
||||
#
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
# Adapted from patch submitted to SourceForge by Steven J. Burr
|
||||
class MacOSX(BaseBrowser):
|
||||
"""Launcher class for Aqua browsers on Mac OS X
|
||||
|
||||
Optionally specify a browser name on instantiation. Note that this
|
||||
will not work for Aqua browsers if the user has moved the application
|
||||
package after installation.
|
||||
|
||||
If no browser is specified, the default browser, as specified in the
|
||||
Internet System Preferences panel, will be used.
|
||||
"""
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
class MacOSXOSAScript(BaseBrowser):
|
||||
def __init__(self, name='default'):
|
||||
super().__init__(name)
|
||||
|
||||
def open(self, url, new=0, autoraise=True):
|
||||
sys.audit("webbrowser.open", url)
|
||||
assert "'" not in url
|
||||
# hack for local urls
|
||||
if not ':' in url:
|
||||
url = 'file:'+url
|
||||
|
||||
# new must be 0 or 1
|
||||
new = int(bool(new))
|
||||
if self.name == "default":
|
||||
# User called open, open_new or get without a browser parameter
|
||||
script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
|
||||
url = url.replace('"', '%22')
|
||||
if self.name == 'default':
|
||||
script = f'open location "{url}"' # opens in default browser
|
||||
else:
|
||||
# User called get and chose a browser
|
||||
if self.name == "OmniWeb":
|
||||
toWindow = ""
|
||||
else:
|
||||
# Include toWindow parameter of OpenURL command for browsers
|
||||
# that support it. 0 == new window; -1 == existing
|
||||
toWindow = "toWindow %d" % (new - 1)
|
||||
cmd = 'OpenURL "%s"' % url.replace('"', '%22')
|
||||
script = '''tell application "%s"
|
||||
activate
|
||||
%s %s
|
||||
end tell''' % (self.name, cmd, toWindow)
|
||||
# Open pipe to AppleScript through osascript command
|
||||
osapipe = os.popen("osascript", "w")
|
||||
if osapipe is None:
|
||||
return False
|
||||
# Write script to osascript's stdin
|
||||
osapipe.write(script)
|
||||
rc = osapipe.close()
|
||||
return not rc
|
||||
|
||||
class MacOSXOSAScript(BaseBrowser):
|
||||
def __init__(self, name):
|
||||
self._name = name
|
||||
|
||||
def open(self, url, new=0, autoraise=True):
|
||||
if self._name == 'default':
|
||||
script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
|
||||
else:
|
||||
script = '''
|
||||
tell application "%s"
|
||||
script = f'''
|
||||
tell application "{self.name}"
|
||||
activate
|
||||
open location "%s"
|
||||
open location "{url}"
|
||||
end
|
||||
'''%(self._name, url.replace('"', '%22'))
|
||||
'''
|
||||
|
||||
osapipe = os.popen("osascript", "w")
|
||||
if osapipe is None:
|
||||
@@ -688,30 +615,96 @@ if sys.platform == 'darwin':
|
||||
rc = osapipe.close()
|
||||
return not rc
|
||||
|
||||
#
|
||||
# Platform support for iOS
|
||||
#
|
||||
if sys.platform == "ios":
|
||||
from _ios_support import objc
|
||||
if objc:
|
||||
# If objc exists, we know ctypes is also importable.
|
||||
from ctypes import c_void_p, c_char_p, c_ulong
|
||||
|
||||
def main():
|
||||
import getopt
|
||||
usage = """Usage: %s [-n | -t] url
|
||||
-n: open new window
|
||||
-t: open new tab""" % sys.argv[0]
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'ntd')
|
||||
except getopt.error as msg:
|
||||
print(msg, file=sys.stderr)
|
||||
print(usage, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
new_win = 0
|
||||
for o, a in opts:
|
||||
if o == '-n': new_win = 1
|
||||
elif o == '-t': new_win = 2
|
||||
if len(args) != 1:
|
||||
print(usage, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
class IOSBrowser(BaseBrowser):
|
||||
def open(self, url, new=0, autoraise=True):
|
||||
sys.audit("webbrowser.open", url)
|
||||
# If ctypes isn't available, we can't open a browser
|
||||
if objc is None:
|
||||
return False
|
||||
|
||||
url = args[0]
|
||||
open(url, new_win)
|
||||
# All the messages in this call return object references.
|
||||
objc.objc_msgSend.restype = c_void_p
|
||||
|
||||
# This is the equivalent of:
|
||||
# NSString url_string =
|
||||
# [NSString stringWithCString:url.encode("utf-8")
|
||||
# encoding:NSUTF8StringEncoding];
|
||||
NSString = objc.objc_getClass(b"NSString")
|
||||
constructor = objc.sel_registerName(b"stringWithCString:encoding:")
|
||||
objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_char_p, c_ulong]
|
||||
url_string = objc.objc_msgSend(
|
||||
NSString,
|
||||
constructor,
|
||||
url.encode("utf-8"),
|
||||
4, # NSUTF8StringEncoding = 4
|
||||
)
|
||||
|
||||
# Create an NSURL object representing the URL
|
||||
# This is the equivalent of:
|
||||
# NSURL *nsurl = [NSURL URLWithString:url];
|
||||
NSURL = objc.objc_getClass(b"NSURL")
|
||||
urlWithString_ = objc.sel_registerName(b"URLWithString:")
|
||||
objc.objc_msgSend.argtypes = [c_void_p, c_void_p, c_void_p]
|
||||
ns_url = objc.objc_msgSend(NSURL, urlWithString_, url_string)
|
||||
|
||||
# Get the shared UIApplication instance
|
||||
# This code is the equivalent of:
|
||||
# UIApplication shared_app = [UIApplication sharedApplication]
|
||||
UIApplication = objc.objc_getClass(b"UIApplication")
|
||||
sharedApplication = objc.sel_registerName(b"sharedApplication")
|
||||
objc.objc_msgSend.argtypes = [c_void_p, c_void_p]
|
||||
shared_app = objc.objc_msgSend(UIApplication, sharedApplication)
|
||||
|
||||
# Open the URL on the shared application
|
||||
# This code is the equivalent of:
|
||||
# [shared_app openURL:ns_url
|
||||
# options:NIL
|
||||
# completionHandler:NIL];
|
||||
openURL_ = objc.sel_registerName(b"openURL:options:completionHandler:")
|
||||
objc.objc_msgSend.argtypes = [
|
||||
c_void_p, c_void_p, c_void_p, c_void_p, c_void_p
|
||||
]
|
||||
# Method returns void
|
||||
objc.objc_msgSend.restype = None
|
||||
objc.objc_msgSend(shared_app, openURL_, ns_url, None, None)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def parse_args(arg_list: list[str] | None):
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Open URL in a web browser.")
|
||||
parser.add_argument("url", help="URL to open")
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("-n", "--new-window", action="store_const",
|
||||
const=1, default=0, dest="new_win",
|
||||
help="open new window")
|
||||
group.add_argument("-t", "--new-tab", action="store_const",
|
||||
const=2, default=0, dest="new_win",
|
||||
help="open new tab")
|
||||
|
||||
args = parser.parse_args(arg_list)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main(arg_list: list[str] | None = None):
|
||||
args = parse_args(arg_list)
|
||||
|
||||
open(args.url, args.new_win)
|
||||
|
||||
print("\a")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user