-
-
Notifications
You must be signed in to change notification settings - Fork 30.8k
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
bpo-24241: Preferred X browser #85
Conversation
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA). Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA. This is necessary for legal reasons before we can look at your contribution. Please follow these steps to help rectify the issue:
Thanks again to your contribution and we look forward to looking at it! |
I've tied my bpo 'daves' account to github 'davesteele'. The contributor form was received 2015-07-17.00:00:00. |
Codecov Report
@@ Coverage Diff @@
## master #85 +/- ##
==========================================
- Coverage 82.38% 82.38% -0.01%
==========================================
Files 1428 1428
Lines 351138 351143 +5
==========================================
- Hits 289291 289275 -16
- Misses 61847 61868 +21 Continue to review full report at Codecov.
|
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.
Found improvements to the code.
Lib/webbrowser.py
Outdated
cmd = "xdg-settings get default-web-browser".split() | ||
_preferred_browser = subprocess.check_output(cmd).decode().strip() | ||
except (FileNotFoundError, subprocess.CalledProcessError): | ||
pass |
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 am not sure you should pass
the exceptions silently.
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'm not sure what to report, otherwise. Passing on the FileNotFoundError feels right to me.
I could drop catching CalledProcessError?
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.
You could use shutil.which to determine if xdg-settings exists before trying to call it (LBYL). But here ignoring the exception (EAFP) seems right to me.
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.
With a _preferred_browsers
set, this would become:
try:
cmd = "xdg-settings get default-web-browser".split()
_xdg_default_browser = subprocess.check_output(cmd).decode().strip()
except (FileNotFoundError, subprocess.CalledProcessError):
pass
else:
_preferred_browsers.add(_xdg_default_browser)
I believe that would be more self-explanatory as to the purpose of ignoring the exceptions here (if we fail to find the xdg default browser, we simply don't add it to the preferred browser set)
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 will suggest updating the documentation for method register
at Doc/library/webbrowser.rst
Lib/webbrowser.py
Outdated
|
||
def register(name, klass, instance=None, update_tryorder=1): | ||
"""Register a browser connector and, optionally, connection.""" | ||
def register(name, klass, instance=None, priority=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 wonder if it’s good to change the signature here. Is it impossible to give high priority to the browser found with xdg-settings, if any, with the update_tryorder param?
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 took the opportunity to make what I thought was an improvement. The tri-state definition of the old parameter seemed a bit clumsy and unnecessary (note that it was left out of the documentation).
I can back out the change to update_tryorder if it is deemed appropriate.
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'd suggest making the parameter preferred
rather than priority
and making it keyword-only.
I've added documentation for the register() priority argument. |
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 like the general idea of this patch, but think it would be more self-explanatory with some changes to the specific implementation details.
Lib/webbrowser.py
Outdated
@@ -15,14 +15,15 @@ class Error(Exception): | |||
|
|||
_browsers = {} # Dictionary of available browser controllers | |||
_tryorder = [] # Preference order of available browsers | |||
_preferred_browser = None # The preferred browser for the current environment |
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.
Rather than only allowing a single preferred browser, perhaps it would make sense to make this a _preferred_browsers
set? That would have a ripple effect on the other updates below.
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.
My original implementation did this, but I didn't find the enhancement useful. There weren't any ripples.
That said, I'll make the change if this is considered a blocker.
Lib/webbrowser.py
Outdated
if update_tryorder > 0: | ||
_tryorder.append(name) | ||
elif update_tryorder < 0: | ||
if priority or (_preferred_browser and name in _preferred_browser): |
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.
With a set, this condition would become if priority or name in _preferred_browsers:
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.
'preferred' is a better name. I'll make the change.
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 had managed to make it to this point without encountering the keyword-only concept in the library (or elsewhere, for that matter). Would this cause unnecessary confusion?
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.
Note that 'name in _preferred_browsers' is not enough. xdg-settings returns e.g. 'firefox.desktop'.
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.
keyword-only is our preferred approach to handling boolean arguments in Python 3, as otherwise you end up with code that's ambiguous at the point of calling (as here, where the True
conveys no info to a reader that doesn't already know the signature, while preferred=True
is a lot more descriptive)
Regarding _preferred_browser
potentially needing to match things like firefox
against firefox.desktop
, that belongs in an explanatory comment in the code itself. I'm fine with dropping the idea of switching to a set, though.
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.
A slight name tweak may help convey the difference between this field and the new parameter to register
though: what do you think about renaming it to _os_preferred_browser
?
Lib/webbrowser.py
Outdated
cmd = "xdg-settings get default-web-browser".split() | ||
_preferred_browser = subprocess.check_output(cmd).decode().strip() | ||
except (FileNotFoundError, subprocess.CalledProcessError): | ||
pass |
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.
With a _preferred_browsers
set, this would become:
try:
cmd = "xdg-settings get default-web-browser".split()
_xdg_default_browser = subprocess.check_output(cmd).decode().strip()
except (FileNotFoundError, subprocess.CalledProcessError):
pass
else:
_preferred_browsers.add(_xdg_default_browser)
I believe that would be more self-explanatory as to the purpose of ignoring the exceptions here (if we fail to find the xdg default browser, we simply don't add it to the preferred browser set)
Lib/webbrowser.py
Outdated
|
||
def register(name, klass, instance=None, update_tryorder=1): | ||
"""Register a browser connector and, optionally, connection.""" | ||
def register(name, klass, instance=None, priority=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'd suggest making the parameter preferred
rather than priority
and making it keyword-only.
Lib/webbrowser.py
Outdated
register("firefox", None, MacOSXOSAScript('firefox'), -1) | ||
register("chrome", None, MacOSXOSAScript('chrome'), -1) | ||
register("MacOSX", None, MacOSXOSAScript('default'), -1) | ||
register("safari", None, MacOSXOSAScript('safari'), True) |
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.
With the above suggested change to the signature of register
, this and the other options below would become:
register("safari", None, MacOSXOSAScript('safari'), preferred=True)
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'll add the 'else' construct.
Codecov Report
@@ Coverage Diff @@
## master #85 +/- ##
==========================================
- Coverage 82.38% 82.37% -0.01%
==========================================
Files 1428 1428
Lines 351138 351206 +68
==========================================
+ Hits 289291 289317 +26
- Misses 61847 61889 +42 Continue to review full report at Codecov.
|
@ncoghlan, I've made some changes based on your review, and pushed back on a few others. You be the final arbiter on the issues. Let me know. |
+1 on leaving the helper variable as a single string - I've suggested a name change and an additional explanatory comment to cover some of the subtleties for future maintainers. For the The commit message also still needs adjustment to better cover the changes being made (i.e. both the If you're so inclined, you could also add an entry to the 3.7 What's New for this change (otherwise I'll just add it along with the NEWS entry when pushing the change) |
Replace the existing undocumented tri-state 'try_order' parameter with the boolean keyword-only 'preferred' parameter. Setting it to True places the browser at the front of the list, preferring it as the return to a subsequent get() call. 'preferred' is added to the documentation.
The traditional first entry in the tryorder queue is xdg-open, which doesn't support new window. Make the default browser first in the try list, to properly support these options.
795ee09
to
871455f
Compare
@ncoghlan, I've reworked, reworded, and rebased the commits based on your suggestions. |
The codecov complaints are due to CI not running on Mac OS X, so this looks good to me now - merging! |
Thanks for your help on this. |
Added the sprint label, as this PR was submitted at the PyCon Pune 2017 core development sprint. |
…WatchdogEx Fix issue python#85: don't schedule already scheduled tasklets. Ensure that the internal watchdog list is empty before and after all test cases of class TestNewWatchdog. Fix test case TestNewWatchdog.test_watchdog_priority_{hard|soft}. The timeout value was much to short to build up a list of watchdogs. The new test cases TestNewWatchdog.test_schedule_deeper_{hard|soft} triggers the assertion failure reliably. They are skipped for now. https://bitbucket.org/stackless-dev/stackless/issues/85 (grafted from 2235702eb90cb0709dd40544b3952a956f2032a9 and 3fb55ce5b2d4)
Sometimes we need to acquire locks before the current PyThreadState is set. This moves the waiter data to it's own struct. It's shared between PyThreadStates on the same thread, such as in multi-interpreter settings. Fixes #85
Add github workflow to build and publish docker image
When calling webbrowser.open*(), the module goes through a list of installed browsers, and uses the first one that succeeds, to process the request.
The first 'browsers' in the 'X' list are 'xdg-open' and others of that ilk. The problem is that they only have one 'open' behavior - the 'new' parameter is ignored ('same window', 'new window', 'new tab').
These commits move the default web browser to the front of the tryorder list, allowing get() behavior to match the documentation.
This same problem was recently fixed in Windows via Issue #8232