diff -r a21ddb1c41d2 Lib/webbrowser.py --- a/Lib/webbrowser.py Fri Aug 22 10:28:42 2014 -0400 +++ b/Lib/webbrowser.py Tue Aug 26 15:57:29 2014 +0200 @@ -54,18 +54,21 @@ def get(using=None): # It is recommended one does "import webbrowser" and uses webbrowser.open(url) # instead of "from webbrowser import *". -def open(url, new=0, autoraise=True): + +def open(url, new=0, autoraise=True, stdout=True, stderr=True): for name in _tryorder: browser = get(name) - if browser.open(url, new, autoraise): + if browser.open(url, new, autoraise, stdout, stderr): return True return False -def open_new(url): - return open(url, 1) -def open_new_tab(url): - return open(url, 2) +def open_new(url, stdout=True, stderr=True): + return open(url, 1, stdout=stdout, stderr=stderr) + + +def open_new_tab(url, stdout=True, stderr=True): + return open(url, 2, stdout=stdout, stderr=stderr) def _synthesize(browser, update_tryorder=1): @@ -100,6 +103,24 @@ def _synthesize(browser, update_tryorder return [None, None] +def _manage_fds(fds): + """Manage the file descriptors redirection: + * True -> None (subprocess inherits file descriptors from + the parent process) + * None or False -> /dev/null + * any other value follows subprocess documentation + """ + # from subprocess module documentation: + # With the default settings of None, no redirection will occur; + # the child's file handles will be inherited from the parent. + if fds is True: + fds = None + elif fds is None or fds is False: + fds = subprocess.DEVNULL + + return fds + + # General parent classes class BaseBrowser(object): @@ -111,14 +132,14 @@ class BaseBrowser(object): self.name = name self.basename = name - def open(self, url, new=0, autoraise=True): + def open(self, url, new=0, autoraise=True, stdout=True, stderr=True): raise NotImplementedError - def open_new(self, url): - return self.open(url, 1) + def open_new(self, url, stdout=True, stderr=True): + return self.open(url, 1, stdout=stdout, stderr=stderr) - def open_new_tab(self, url): - return self.open(url, 2) + def open_new_tab(self, url, stdout=True, stderr=True): + return self.open(url, 2, stdout=stdout, stderr=stderr) class GenericBrowser(BaseBrowser): @@ -135,14 +156,28 @@ class GenericBrowser(BaseBrowser): self.args = name[1:] self.basename = os.path.basename(self.name) - def open(self, url, new=0, autoraise=True): + def open(self, + url, + new=0, + autoraise=True, + stdout=True, + stderr=True + ): cmdline = [self.name] + [arg.replace("%s", url) for arg in self.args] + + redirect_stdout = _manage_fds(stdout) + redirect_stderr = _manage_fds(stderr) + try: if sys.platform[:3] == 'win': p = subprocess.Popen(cmdline) else: - p = subprocess.Popen(cmdline, close_fds=True) + p = subprocess.Popen(cmdline, + close_fds=True, + stdout=redirect_stdout, + stderr=redirect_stderr + ) return not p.wait() except OSError: return False @@ -152,9 +187,19 @@ class BackgroundBrowser(GenericBrowser): """Class for all browsers which are to be started in the background.""" - def open(self, url, new=0, autoraise=True): + def open(self, + url, + new=0, + autoraise=True, + stdout=True, + stderr=True + ): cmdline = [self.name] + [arg.replace("%s", url) for arg in self.args] + + redirect_stdout = _manage_fds(stdout) + redirect_stderr = _manage_fds(stderr) + try: if sys.platform[:3] == 'win': p = subprocess.Popen(cmdline) @@ -162,7 +207,12 @@ class BackgroundBrowser(GenericBrowser): setsid = getattr(os, 'setsid', None) if not setsid: setsid = getattr(os, 'setpgrp', None) - p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid) + p = subprocess.Popen(cmdline, + close_fds=True, + stdout=redirect_stdout, + stderr=redirect_stderr, + preexec_fn=setsid + ) return (p.poll() is None) except OSError: return False @@ -173,7 +223,6 @@ class UnixBrowser(BaseBrowser): raise_opts = None background = False - redirect_stdout = True # In remote_args, %s will be replaced with the requested URL. %action will # be replaced depending on the value of 'new' passed to open. # remote_action is used for new=0 (open). If newwin is not None, it is @@ -185,13 +234,14 @@ class UnixBrowser(BaseBrowser): remote_action_newwin = None remote_action_newtab = None - def _invoke(self, args, remote, autoraise): + def _invoke(self, args, remote, autoraise, stdout, stderr): raise_opt = [] if remote and self.raise_opts: # 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 @@ -200,9 +250,10 @@ class UnixBrowser(BaseBrowser): else: # for TTY browsers, we need stdin/out inout = None + p = subprocess.Popen(cmdline, close_fds=True, stdin=inout, - stdout=(self.redirect_stdout and inout or None), - stderr=inout, start_new_session=True) + stdout=stdout, stderr=stderr, + start_new_session=True) if remote: # wait at most five seconds. If the subprocess is not finished, the # remote invocation has (hopefully) started a new instance. @@ -220,7 +271,13 @@ class UnixBrowser(BaseBrowser): else: return not p.wait() - def open(self, url, new=0, autoraise=True): + def open(self, + url, + new=0, + autoraise=True, + stdout=True, + stderr=True + ): if new == 0: action = self.remote_action elif new == 1: @@ -237,7 +294,15 @@ class UnixBrowser(BaseBrowser): args = [arg.replace("%s", url).replace("%action", action) for arg in self.remote_args] args = [arg for arg in args if arg] - success = self._invoke(args, True, autoraise) + + redirect_stdout = _manage_fds(stdout) + redirect_stderr = _manage_fds(stderr) + + success = self._invoke(args, + True, + autoraise, + redirect_stdout, + redirect_stderr) if not success: # remote invocation failed, try straight way args = [arg.replace("%s", url) for arg in self.args] @@ -313,7 +378,7 @@ class Konqueror(BaseBrowser): for more information on the Konqueror remote-control interface. """ - def open(self, url, new=0, autoraise=True): + def open(self, url, new=0, autoraise=True, stdout=True, stderr=True): # XXX Currently I know no way to prevent KFM from opening a new win. if new == 2: action = "newTab"