cinap_lenrekadd hg and python4bb9e2215 years agoHistory
651 lines · 20.1 KB · Python
RawDownload
#! /usr/bin/env python"""Interfaces for launching and remotely controlling Web browsers.""" import osimport shleximport sysimport statimport subprocessimport time __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]    if update_tryorder > 0:        _tryorder.append(name)    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:        alternatives = [using]    else:        alternatives = _tryorder    for browser in alternatives:        if '%s' in browser:            # User gave us a command line, split it into name and args            browser = shlex.split(browser)            if browser[-1] == '&':                return BackgroundBrowser(browser[:-1])            else:                return GenericBrowser(browser)        else:            # User gave us a browser name or path.            try:                command = _browsers[browser.lower()]            except KeyError:                command = _synthesize(browser)            if command[1] is not None:                return command[1]            elif command[0] is not 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 *". def open(url, new=0, autoraise=1):    for name in _tryorder:        browser = get(name)        if browser.open(url, new, autoraise):            return True    return False def open_new(url):    return open(url, 1) def open_new_tab(url):    return open(url, 2)  def _synthesize(browser, update_tryorder=1):    """Attempt to synthesize a controller base on existing controllers.     This is useful to create a controller when a user specifies a path to    an entry in the BROWSER environment variable -- we can copy a general    controller to operate using a specific installation of the desired    browser in this way.     If we can't create a controller in this way, or if there is no    executable for the requested browser, return [None, None].     """    cmd = browser.split()[0]    if not _iscommand(cmd):        return [None, None]    name = os.path.basename(cmd)    try:        command = _browsers[name.lower()]    except KeyError:        return [None, None]    # now attempt to clone to fit the new name:    controller = command[1]    if controller and name.lower() == controller.basename:        import copy        controller = copy.copy(controller)        controller.name = browser        controller.basename = os.path.basename(browser)        register(browser, None, controller, update_tryorder)        return [None, controller]    return [None, None]  if sys.platform[:3] == "win":    def _isexecutable(cmd):        cmd = cmd.lower()        if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):            return True        for ext in ".exe", ".bat":            if os.path.isfile(cmd + ext):                return True        return Falseelse:    def _isexecutable(cmd):        if os.path.isfile(cmd):            mode = os.stat(cmd)[stat.ST_MODE]            if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:                return True        return False def _iscommand(cmd):    """Return True if cmd is executable or can be found on the executable    search path."""    if _isexecutable(cmd):        return True    path = os.environ.get("PATH")    if not path:        return False    for d in path.split(os.pathsep):        exe = os.path.join(d, cmd)        if _isexecutable(exe):            return True    return False  # General parent classes class BaseBrowser(object):    """Parent class for all browsers. Do not use directly."""     args = ['%s']     def __init__(self, name=""):        self.name = name        self.basename = name     def open(self, url, new=0, autoraise=1):        raise NotImplementedError     def open_new(self, url):        return self.open(url, 1)     def open_new_tab(self, url):        return self.open(url, 2)  class GenericBrowser(BaseBrowser):    """Class for all browsers started with a command       and without remote functionality."""     def __init__(self, name):        if isinstance(name, basestring):            self.name = name        else:            # name should be a list with arguments            self.name = name[0]            self.args = name[1:]        self.basename = os.path.basename(self.name)     def open(self, url, new=0, autoraise=1):        cmdline = [self.name] + [arg.replace("%s", url)                                 for arg in self.args]        try:            if sys.platform[:3] == 'win':                p = subprocess.Popen(cmdline)            else:                p = subprocess.Popen(cmdline, close_fds=True)            return not p.wait()        except OSError:            return False  class BackgroundBrowser(GenericBrowser):    """Class for all browsers which are to be started in the       background."""     def open(self, url, new=0, autoraise=1):        cmdline = [self.name] + [arg.replace("%s", url)                                 for arg in self.args]        try:            if sys.platform[:3] == 'win':                p = subprocess.Popen(cmdline)            else:                setsid = getattr(os, 'setsid', None)                if not setsid:                    setsid = getattr(os, 'setpgrp', None)                p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)            return (p.poll() is None)        except OSError:            return False  class UnixBrowser(BaseBrowser):    """Parent class for all Unix browsers with remote functionality."""     raise_opts = None    remote_args = ['%action', '%s']    remote_action = None    remote_action_newwin = None    remote_action_newtab = None    background = False    redirect_stdout = True     def _invoke(self, args, remote, autoraise):        raise_opt = []        if remote and self.raise_opts:            # use autoraise argument only for remote invocation            autoraise = int(bool(autoraise))            opt = self.raise_opts[autoraise]            if opt: raise_opt = [opt]         cmdline = [self.name] + raise_opt + args         if remote or self.background:            inout = file(os.devnull, "r+")        else:            # for TTY browsers, we need stdin/out            inout = None        # if possible, put browser in separate process group, so        # keyboard interrupts don't affect browser as well as Python        setsid = getattr(os, 'setsid', None)        if not setsid:            setsid = getattr(os, 'setpgrp', None)         p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,                             stdout=(self.redirect_stdout and inout or None),                             stderr=inout, preexec_fn=setsid)        if remote:            # wait five secons. If the subprocess is not finished, the            # remote invocation has (hopefully) started a new instance.            time.sleep(1)            rc = p.poll()            if rc is None:                time.sleep(4)                rc = p.poll()                if rc is None:                    return True            # if remote call failed, open() will try direct invocation            return not rc        elif self.background:            if p.poll() is None:                return True            else:                return False        else:            return not p.wait()     def open(self, url, new=0, autoraise=1):        if new == 0:            action = self.remote_action        elif new == 1:            action = self.remote_action_newwin        elif new == 2:            if self.remote_action_newtab is None:                action = self.remote_action_newwin            else:                action = self.remote_action_newtab        else:            raise Error("Bad 'new' parameter to open(); " +                        "expected 0, 1, or 2, got %s" % new)         args = [arg.replace("%s", url).replace("%action", action)                for arg in self.remote_args]        success = self._invoke(args, True, autoraise)        if not success:            # remote invocation failed, try straight way            args = [arg.replace("%s", url) for arg in self.args]            return self._invoke(args, False, False)        else:            return True  class Mozilla(UnixBrowser):    """Launcher class for Mozilla/Netscape browsers."""     raise_opts = ["-noraise", "-raise"]     remote_args = ['-remote', 'openURL(%s%action)']    remote_action = ""    remote_action_newwin = ",new-window"    remote_action_newtab = ",new-tab"     background = True Netscape = Mozilla  class Galeon(UnixBrowser):    """Launcher class for Galeon/Epiphany browsers."""     raise_opts = ["-noraise", ""]    remote_args = ['%action', '%s']    remote_action = "-n"    remote_action_newwin = "-w"     background = True  class Opera(UnixBrowser):    "Launcher class for Opera browser."     raise_opts = ["", "-raise"]     remote_args = ['-remote', 'openURL(%s%action)']    remote_action = ""    remote_action_newwin = ",new-window"    remote_action_newtab = ",new-page"    background = True  class Elinks(UnixBrowser):    "Launcher class for Elinks browsers."     remote_args = ['-remote', 'openURL(%s%action)']    remote_action = ""    remote_action_newwin = ",new-window"    remote_action_newtab = ",new-tab"    background = False     # elinks doesn't like its stdout to be redirected -    # it uses redirected stdout as a signal to do -dump    redirect_stdout = False  class Konqueror(BaseBrowser):    """Controller for the KDE File Manager (kfm, or Konqueror).     See the output of ``kfmclient --commands``    for more information on the Konqueror remote-control interface.    """     def open(self, url, new=0, autoraise=1):        # XXX Currently I know no way to prevent KFM from opening a new win.        if new == 2:            action = "newTab"        else:            action = "openURL"         devnull = file(os.devnull, "r+")        # if possible, put browser in separate process group, so        # keyboard interrupts don't affect browser as well as Python        setsid = getattr(os, 'setsid', None)        if not setsid:            setsid = getattr(os, 'setpgrp', None)         try:            p = subprocess.Popen(["kfmclient", action, url],                                 close_fds=True, stdin=devnull,                                 stdout=devnull, stderr=devnull)        except OSError:            # fall through to next variant            pass        else:            p.wait()            # kfmclient's return code unfortunately has no meaning as it seems            return True         try:            p = subprocess.Popen(["konqueror", "--silent", url],                                 close_fds=True, stdin=devnull,                                 stdout=devnull, stderr=devnull,                                 preexec_fn=setsid)        except OSError:            # fall through to next variant            pass        else:            if p.poll() is None:                # Should be running now.                return True         try:            p = subprocess.Popen(["kfm", "-d", url],                                 close_fds=True, stdin=devnull,                                 stdout=devnull, stderr=devnull,                                 preexec_fn=setsid)        except OSError:            return False        else:            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(tempdir, 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 socket.error:                # no good; attempt to clean it out, but don't fail:                try:                    os.unlink(fn)                except IOError:                    pass            else:                return s     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=1):        if new:            ok = self._remote("LOADNEW " + url)        else:            ok = self._remote("LOAD " + url)        return ok  ## Platform support for Unix# # These are the right tests because all these Unix browsers require either# a console terminal or an X display to run. def register_X_browsers():    # The default Gnome browser    if _iscommand("gconftool-2"):        # get the web browser string from gconftool        gc = 'gconftool-2 -g /desktop/gnome/url-handlers/http/command 2>/dev/null'        out = os.popen(gc)        commd = out.read().strip()        retncode = out.close()         # if successful, register it        if retncode is None and commd:            register("gnome", None, BackgroundBrowser(commd.split()))     # First, the Mozilla/Netscape browsers    for browser in ("mozilla-firefox", "firefox",                    "mozilla-firebird", "firebird",                    "seamonkey", "mozilla", "netscape"):        if _iscommand(browser):            register(browser, None, Mozilla(browser))     # Konqueror/kfm, the KDE browser.    if _iscommand("kfm"):        register("kfm", Konqueror, Konqueror("kfm"))    elif _iscommand("konqueror"):        register("konqueror", Konqueror, Konqueror("konqueror"))     # Gnome's Galeon and Epiphany    for browser in ("galeon", "epiphany"):        if _iscommand(browser):            register(browser, None, Galeon(browser))     # Skipstone, another Gtk/Mozilla based browser    if _iscommand("skipstone"):        register("skipstone", None, BackgroundBrowser("skipstone"))     # Opera, quite popular    if _iscommand("opera"):        register("opera", None, Opera("opera"))     # Next, Mosaic -- old but still in use.    if _iscommand("mosaic"):        register("mosaic", None, BackgroundBrowser("mosaic"))     # Grail, the Python browser. Does anybody still use it?    if _iscommand("grail"):        register("grail", Grail, None) # Prefer X browsers if presentif os.environ.get("DISPLAY"):    register_X_browsers() # Also try console browsersif os.environ.get("TERM"):    # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>    if _iscommand("links"):        register("links", None, GenericBrowser("links"))    if _iscommand("elinks"):

Lines 1–500 of 651.