-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
gh-105983: Add private mode support to webbrowser #105984
Changes from all commits
814309a
2a67e33
9d2fcfe
46d06d4
7baebe5
b9079f4
f04f255
7eaa455
9e4ee59
333c0e7
4872893
308e060
fd0e6a3
e952efe
ade902e
7c28c35
6597494
8975499
2b9a280
545784d
f1a20fb
ae0602b
9312844
9e9811b
a4657c7
0f4b11f
9580689
3684723
2d7a9c5
993f387
9bb7124
2130631
f8364db
d1de67c
994fe23
a43bd63
3cd8d82
5f0afcb
9408b3a
394265c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ class Error(Exception): | |
_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: | ||
|
@@ -75,6 +76,7 @@ def open(url, new=0, autoraise=True): | |
- 0: the same browser window (the default). | ||
- 1: a new browser window. | ||
- 2: a new browser page ("tab"). | ||
- 3: an incognito/ private browser. | ||
If possible, autoraise raises the window (the default) or not. | ||
""" | ||
if _tryorder is None: | ||
|
@@ -101,6 +103,12 @@ def open_new_tab(url): | |
""" | ||
return open(url, 2) | ||
|
||
def open_incognito(url): | ||
"""Open url in incognito mode of the default browser. | ||
|
||
If not possible, then the behavior becomes equivalent to open_new(). | ||
""" | ||
return open(url, 3) | ||
|
||
def _synthesize(browser, *, preferred=False): | ||
"""Attempt to synthesize a controller based on existing controllers. | ||
|
@@ -154,6 +162,9 @@ def open_new(self, url): | |
def open_new_tab(self, url): | ||
return self.open(url, 2) | ||
|
||
def open_incognito(self, url): | ||
return self.open(url, 3) | ||
|
||
|
||
class GenericBrowser(BaseBrowser): | ||
"""Class for all browsers started with a command | ||
|
@@ -218,6 +229,7 @@ class UnixBrowser(BaseBrowser): | |
remote_action = None | ||
remote_action_newwin = None | ||
remote_action_newtab = None | ||
remote_action_incognito = "" | ||
|
||
def _invoke(self, args, remote, autoraise, url=None): | ||
raise_opt = [] | ||
|
@@ -265,9 +277,11 @@ def open(self, url, new=0, autoraise=True): | |
action = self.remote_action_newwin | ||
else: | ||
action = self.remote_action_newtab | ||
elif new == 3: | ||
action = self.remote_action_incognito | ||
else: | ||
raise Error("Bad 'new' parameter to open(); " + | ||
"expected 0, 1, or 2, got %s" % new) | ||
f"expected 0, 1, 2, or 3, got {new}") | ||
|
||
args = [arg.replace("%s", url).replace("%action", action) | ||
for arg in self.remote_args] | ||
|
@@ -288,6 +302,7 @@ class Mozilla(UnixBrowser): | |
remote_action = "" | ||
remote_action_newwin = "-new-window" | ||
remote_action_newtab = "-new-tab" | ||
remote_action_incognito = "--private-window" | ||
background = True | ||
|
||
|
||
|
@@ -298,6 +313,7 @@ class Epiphany(UnixBrowser): | |
remote_args = ['%action', '%s'] | ||
remote_action = "-n" | ||
remote_action_newwin = "-w" | ||
remote_action_incognito = "--incognito-mode" | ||
background = True | ||
|
||
|
||
|
@@ -308,6 +324,7 @@ class Chrome(UnixBrowser): | |
remote_action = "" | ||
remote_action_newwin = "--new-window" | ||
remote_action_newtab = "" | ||
remote_action_incognito = "--incognito" | ||
background = True | ||
|
||
Chromium = Chrome | ||
|
@@ -397,6 +414,7 @@ class Edge(UnixBrowser): | |
remote_action = "" | ||
remote_action_newwin = "--new-window" | ||
remote_action_newtab = "" | ||
remote_action_incognito = "--inprivate" | ||
background = True | ||
|
||
|
||
|
@@ -588,14 +606,23 @@ def open(self, url, new=0, autoraise=True): | |
return not rc | ||
|
||
|
||
def _get_supported_browsers(): | ||
if _tryorder is None: | ||
register_standard_browsers() | ||
return _browsers.keys() | ||
|
||
|
||
def main(): | ||
import getopt | ||
usage = """Usage: %s [-n | -t | -h] url | ||
usage = """Usage: %s [-l] | [-b browser] [-i | -n | -t | -h] url | ||
-n: open new window | ||
-t: open new tab | ||
-i: open with private mode | ||
-l: list available browsers | ||
-b <browser>: uses <browser> to open | ||
-h, --help: show help""" % sys.argv[0] | ||
try: | ||
opts, args = getopt.getopt(sys.argv[1:], 'ntdh',['help']) | ||
opts, args = getopt.getopt(sys.argv[1:], 'lintb:dh',['help']) | ||
except getopt.error as msg: | ||
print(msg, file=sys.stderr) | ||
print(usage, file=sys.stderr) | ||
|
@@ -604,6 +631,14 @@ def main(): | |
for o, a in opts: | ||
if o == '-n': new_win = 1 | ||
elif o == '-t': new_win = 2 | ||
elif o == "-i": | ||
new_win = 3 | ||
elif o == "-b": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we have If you feel like adding this feature is a good idea (it looks like it might be), please open a new issue: explain the use-case and the "why". But, let's keep this PR simple: only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is related to the cmdline interface when webbroser main() func is called. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not resolved at all. These are separate new additions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do appreciate your contribution here! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for me this is a migration topic. the experts do know better what to do here. myself i dont see here in the working loop since i dont have this detailed knowledge of what being best on master code baseline There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don’t understand «migration». There are clear requests to remove some code that was added, to keep this PR focused on one thing. If you don’t want to pursue it, then someone else will have to volunteer. |
||
browser = a | ||
elif o == "-l": | ||
for nam in _get_supported_browsers(): | ||
print(nam) | ||
sys.exit() | ||
elif o == '-h' or o == '--help': | ||
print(usage, file=sys.stderr) | ||
sys.exit() | ||
|
@@ -612,7 +647,15 @@ def main(): | |
sys.exit(1) | ||
|
||
url = args[0] | ||
open(url, new_win) | ||
if browser: | ||
try: | ||
br = get(browser) | ||
except: | ||
print(f"browser {browser} not found", file=sys.stderr) | ||
sys.exit(1) | ||
br.open(url, new_win) | ||
else: | ||
open(url, new_win) | ||
|
||
print("\a") | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Add support for incognito / private browsing for :func:`webbrowser.open`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Falling back to "open_new" feels like "errors passing silently" to me. Why was this choice made?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a user of the API I'd be disappointed if an URL is opened in regular mode when I explicitly asked to open it in incognito mode. I'd therefore prefer to raise an exception when incognito mode isn't available.
For use cases where incognito mode is preferred but optional the user can code this themselves, whereas you cannot build my preferred semantics on top of the current implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two API use cases are different: open_new_tab vs open_new is "just" a different look and feel. "open_incognito" has semantic differences in the browser state (such as not storing the page in long term history and separate cookie stores). Opening a page in a regular window when an incognito window was requested can result in unwanted side effects.
I wouldn't mind a keyword argument to enable a fallback to open_new, but it shouldn't be the default. Just like
exists_ok
defaults to false inos.makedirs
. Something likedef open_incognito(url, *, regular_ok=False)
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @ronaldoussoren: if a user is asking to keep their private URL secret, we must not "leak" that to the open internet, which could have extremely serious consequences for some people. Let's raise an exception instead.
Regarding Opera: if the user asks for a regular tab and it opens in private tab, that's fine, nothing is leaked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've plenty of time: 3.13 will be released in October 2024 with a May 2024 deadline to get new features in.
For documentation, something like:
@ronaldoussoren has already made the proposal, @kr-g please could you update this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no preference on whether it should be an exception of a separate method or a permissive argument.
I can agree with others about the issue in general only because a user who is supposed to get the incognito mode most likely will be unaware of it. So we have a choice between "always show user a warning in advance" vs "let the application gracefully treat a failure by showing an absolutely relevant warning with instructions (or an error if the very fact of page visitation must not be leaked)". Personally, I prefer the second option.
The exact interface choice is out of my competence.