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:02:48 2014 +0200 @@ -10,12 +10,14 @@ import subprocess __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] + class Error(Exception): pass _browsers = {} # Dictionary of available browser controllers _tryorder = [] # Preference order of available browsers + def register(name, klass, instance=None, update_tryorder=1): """Register a browser connector and, optionally, connection.""" _browsers[name.lower()] = [klass, instance] @@ -24,6 +26,7 @@ def register(name, klass, instance=None, elif update_tryorder < 0: _tryorder.insert(0, name) + def get(using=None): """Return a browser launcher instance appropriate for the environment.""" if using is not None: @@ -54,18 +57,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 +106,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 +135,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 +159,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 +190,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 +210,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 +226,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 +237,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 +253,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 +274,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 +297,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 +381,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" @@ -485,7 +553,8 @@ if os.environ.get("DISPLAY"): if os.environ.get("TERM"): if shutil.which("www-browser"): register("www-browser", None, GenericBrowser("www-browser")) - # The Links/elinks browsers + # The Links/elinks browsers + # if shutil.which("links"): register("links", None, GenericBrowser("links")) if shutil.which("elinks"): @@ -520,7 +589,8 @@ if sys.platform[:3] == "win": register("windows-default", WindowsDefault) # Detect some common Windows browsers, fallback to IE - iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"), + 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): @@ -556,14 +626,16 @@ if sys.platform == 'darwin': 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 + # opens in default browser + script = 'open location "%s"' % url.replace('"', '%22') 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 + # 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" @@ -585,14 +657,15 @@ if sys.platform == 'darwin': def open(self, url, new=0, autoraise=True): if self._name == 'default': - script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser + # opens in default browser + script = 'open location "%s"' % url.replace('"', '%22') else: script = ''' tell application "%s" activate open location "%s" end - '''%(self._name, url.replace('"', '%22')) + ''' % (self._name, url.replace('"', '%22')) osapipe = os.popen("osascript", "w") if osapipe is None: @@ -602,7 +675,6 @@ if sys.platform == 'darwin': rc = osapipe.close() return not rc - # Don't clear _tryorder or _browsers since OS X can use above Unix support # (but we prefer using the OS X specific stuff) register("safari", None, MacOSXOSAScript('safari'), -1) @@ -623,7 +695,7 @@ if "BROWSER" in os.environ: cmd = _synthesize(cmdline, -1) if cmd[1] is None: register(cmdline, None, GenericBrowser(cmdline), -1) - cmdline = None # to make del work if _userchoices was empty + cmdline = None # to make del work if _userchoices was empty del cmdline del _userchoices @@ -643,8 +715,10 @@ def main(): sys.exit(1) new_win = 0 for o, a in opts: - if o == '-n': new_win = 1 - elif o == '-t': new_win = 2 + if o == '-n': + new_win = 1 + elif o == '-t': + new_win = 2 if len(args) != 1: print(usage, file=sys.stderr) sys.exit(1)