interact.py :  » Development » Leo » Leo-4.7.1-final » leo » plugins » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Leo 
Leo » Leo 4.7.1 final » leo » plugins » interact.py
#@+leo-ver=4-thin
#@+node:tbrown.20090513125417.5244:@thin interact.py
#@@language python
#@@tabwidth -4
#@+others
#@+node:tbrown.20090603104805.4937:interact declarations
"""Add buttons so leo can interact with command line environments.

Currently implements `bash` shell and `psql` (postresql SQL db shell).

Single-line commands can be entered in the headline with a blank body,
multi-line commands can be entered in the body with a descriptive
title in the headline.  Press the `bash` or `psql` button to send
the command to the appropriate interpreter.

The output from the command is **always** stored in a new node added
as the first child of the command node.  For multi-line commands
this new node is selected.  For single-line command this new node
is not shown, instead the body text of the command node is updated
to reflect the most recent output.  Comment delimiter magic is used
to allow single-line and multi-line commands to maintain their
single-line and multi-line flavors.

Both the new child nodes and the updated body text of single-line
commands are timestamped.

For the `bash` button the execution directory is either the directory
containing the `.leo` file, or any other path as specified by ancestor
`@path` nodes.

Currently the `psql` button just connects to the default database.  ";"
is required at the end of SQL statements.

Requires `pexpect` module.
"""
# 0.1 by Terry Brown, 2009-05-12
import leo.core.leoGlobals as g
import leo.core.leoPlugins as leoPlugins
from mod_scripting import scriptingController
import pexpect
import time
import os
#@-node:tbrown.20090603104805.4937:interact declarations
#@+node:tbrown.20090603104805.4938:init
def init():
    leoPlugins.registerHandler('after-create-leo-frame', onCreate)
    g.plugin_signon(__name__)
    return True

#@-node:tbrown.20090603104805.4938:init
#@+node:tbrown.20090603104805.4939:onCreate
def onCreate(tag, keywords):
    InteractController(keywords['c'])
#@-node:tbrown.20090603104805.4939:onCreate
#@+node:tbrown.20090603104805.4940:class Interact
class Interact(object):
    #@    @+others
    #@+node:tbrown.20090603104805.4941:__init__
    def __init__(self, c):
        self.c = c
    #@-node:tbrown.20090603104805.4941:__init__
    #@+node:tbrown.20090603104805.4942:available
    def available(self):
        raise NotImplementedError
    #@-node:tbrown.20090603104805.4942:available
    #@+node:tbrown.20090603104805.4943:run
    def run(self, p):
        raise NotImplementedError
    #@-node:tbrown.20090603104805.4943:run
    #@+node:tbrown.20090603104805.4944:buttonText
    def buttonText(self):
        raise NotImplementedError
    #@-node:tbrown.20090603104805.4944:buttonText
    #@+node:tbrown.20090603104805.4945:statusText
    def statusText(self):
        raise NotImplementedError
    #@-node:tbrown.20090603104805.4945:statusText
    #@-others
#@-node:tbrown.20090603104805.4940:class Interact
#@+node:tbrown.20090603104805.4946:class InteractPSQL
class InteractPSQL(Interact):

    prompt = '__psql-leo__'

    #@    @+others
    #@+node:tbrown.20090603104805.4947:__init__
    def __init__(self, c):
        Interact.__init__(self, c)
        prompts = ' '.join(['--set PROMPT%d=%s'%(i,self.prompt) for i in range(1,4)])
        prompts += ' --pset pager=off'
        self._available = True
        try:
            self.psqlLink = pexpect.spawn('psql %s'%prompts)
            self.leftover = ''
            for i in self.psqlReader(self.psqlLink):
                pass  # eat the initial output as it isn't interesting
        except pexpect.ExceptionPexpect:
            self._available = False

    #@-node:tbrown.20090603104805.4947:__init__
    #@+node:tbrown.20090603104805.4948:available
    def available(self):
        return self._available

    #@-node:tbrown.20090603104805.4948:available
    #@+node:tbrown.20090603104805.4949:buttonText
    def buttonText(self):
        return "psql"
    #@-node:tbrown.20090603104805.4949:buttonText
    #@+node:tbrown.20090603104805.4950:statusText
    def statusText(self):
        return "send headline or body to psql session"

    #@-node:tbrown.20090603104805.4950:statusText
    #@+node:tbrown.20090603104805.4951:run
    def run(self, p):
        c = self.c
        q = p.b
        if not q.strip() or p.b.strip().startswith('--- '):
            q = p.h
        if p.h.strip().startswith('--'):
            q = None

        if q is None:
            g.es('No valid / uncommented query')
        else:
            self.psqlLink.send(q.strip()+'\n')
            ans = []
            maxlen=100
            for d in self.psqlReader(self.psqlLink):
                if d.strip():
                    ans.append(d)
                    if len(ans) > maxlen:
                        del ans[maxlen-10]
                        ans[maxlen-10] = '   ... skipping ...'
            n = p.insertAsNthChild(0)
            n.h = '-- ' + time.asctime()
            n.b = '\n'.join(ans)
            if p.b.strip().startswith('--- ') or not p.b.strip():
                p.b = '-'+n.h+'\n\n'+n.b
                p.contract()
            else:
                c.selectPosition(n)
            c.redraw()

    #@-node:tbrown.20090603104805.4951:run
    #@+node:tbrown.20090603104805.4952:psqlReader
    def psqlReader(self, proc):
        cnt = 0
        timeout = False
        while not timeout:
            dat = []
            try:
                dat = self.leftover + proc.read_nonblocking(size=10240,timeout=1)
                self.leftover = ''
                #X print dat
                if not(dat.endswith('\n')):
                    if '\n' in dat:
                        dat, self.leftover = dat.rsplit('\n',1)
                    else:
                        time.sleep(0.5)
                        self.leftover = dat
                        dat = None
                if dat:
                    dat = dat.split("\n")
            except pexpect.TIMEOUT:
                timeout = True

            #X if self.leftover == self.prompt:
            #X     timeout = True

            if dat:
                for d in dat:
                    cnt += 1
                    #X if d == self.prompt:
                    #X     timeout = True
                    #X else:
                    yield d.replace(self.prompt,'# ') # '%4d: %s' % (cnt,d)
    #@-node:tbrown.20090603104805.4952:psqlReader
    #@-others
#@-node:tbrown.20090603104805.4946:class InteractPSQL
#@+node:tbrown.20090603104805.4953:class InteractBASH
class InteractBASH(Interact):

    prompt = '__bash-leo__'

    #@    @+others
    #@+node:tbrown.20090603104805.4954:__init__
    def __init__(self, c):
        Interact.__init__(self, c)
        self._available = True
        try:
            self.bashLink = pexpect.spawn('bash -i')
            self.bashLink.setwinsize(30,256)
            # stop bash emitting chars for long lines
            self.bashLink.send("PS1='> '\n")
            self.bashLink.send("unalias ls\n")
            self.leftover = ''
            for i in self.bashReader(self.bashLink):
                pass  # eat the initial output as it isn't interesting
                      # and in includes chrs leo can't encode currently
        except pexpect.ExceptionPexpect:
            self._available = False

    #@-node:tbrown.20090603104805.4954:__init__
    #@+node:tbrown.20090603104805.4955:buttonText
    def buttonText(self):
        return "bash"
    #@-node:tbrown.20090603104805.4955:buttonText
    #@+node:tbrown.20090603104805.4956:statusText
    def statusText(self):
        return "send headline or body to bash session"

    #@-node:tbrown.20090603104805.4956:statusText
    #@+node:tbrown.20090603104805.4957:available
    def available(self):
        return self._available

    #@-node:tbrown.20090603104805.4957:available
    #@+node:tbrown.20090603104805.4958:run
    def run(self, p):
        c = self.c
        q = p.b
        if not q.strip() or p.b.strip().startswith('### '):
            q = p.h
        if p.h.strip().startswith('#'):
            q = None

        if q is None:
            g.es('No valid / uncommented statement')
        else:
            path = self.getPath(c, p)
            if not path:
                path = os.path.dirname(c.fileName())
            if path:
                self.bashLink.send('cd %s\n' % path)
            self.bashLink.send(q.strip()+'\n')
            ans = []
            maxlen=100
            for d in self.bashReader(self.bashLink):
                if d.strip():
                    ans.append(d)
                    if len(ans) > maxlen:
                        del ans[maxlen-10]
                        ans[maxlen-10] = '   ... skipping ...'
            n = p.insertAsNthChild(0)
            n.h = '## ' + time.asctime()
            n.b = '\n'.join(ans)
            if p.b.strip().startswith('### ') or not p.b.strip():
                p.b = '#'+n.h+'\n\n'+n.b
                p.contract()
            else:
                c.selectPosition(n)
            c.redraw()

    #@-node:tbrown.20090603104805.4958:run
    #@+node:tbrown.20090603104805.4959:bashReader
    def bashReader(self, proc):
        cnt = 0
        timeout = False
        while not timeout:
            dat = []
            try:
                dat = self.leftover + proc.read_nonblocking(size=10240,timeout=1)
                self.leftover = ''
                #X print dat
                if not(dat.endswith('\n')):
                    if '\n' in dat:
                        dat, self.leftover = dat.rsplit('\n',1)
                    else:
                        time.sleep(0.5)
                        self.leftover = dat
                        dat = None
                if dat:
                    dat = dat.split("\n")
            except pexpect.TIMEOUT:
                timeout = True

            #X if self.leftover == self.prompt:
            #X     timeout = True

            if dat:
                for d in dat:
                    cnt += 1
                    #X if d == self.prompt:
                    #X     timeout = True
                    #X else:
                    yield d.replace(self.prompt,'# ') # '%4d: %s' % (cnt,d)

    #@-node:tbrown.20090603104805.4959:bashReader
    #@+node:tbrown.20090603104805.4960:getPath
    def getPath(self, c, p):
        for n in p.self_and_parents():
            if n.h.startswith('@path'):
                break
        else:
            return None  # must have a full fledged @path in parents

        aList = g.get_directives_dict_list(p)
        path = c.scanAtPathDirectives(aList)
        return path
    #@-node:tbrown.20090603104805.4960:getPath
    #@-others
#@-node:tbrown.20090603104805.4953:class InteractBASH
#@+node:tbrown.20090603104805.4961:class InteractController
class InteractController:

    """quickMove binds to a controller, adds menu entries for
       creating buttons, and creates buttons as needed
    """
    #@    @+others
    #@+node:tbrown.20090603104805.4962:__init__

    def __init__(self, c):

        self.c = c

        #X table = (
        #X     ("Add To First Child Button",None,self.addToFirstChildButton),
        #X     ("Add To Last Child Button",None,self.addToLastChildButton),
        #X )

        #X  c.frame.menu.createMenuItemsFromTable('Outline', table)

        self.addButton(InteractPSQL)
        self.addButton(InteractBASH)
    #@-node:tbrown.20090603104805.4962:__init__
    #@+node:tbrown.20090603104805.4963:addToFirstChildButton
    def addToFirstChildButton (self,event=None):
        self.addButton(first=True)

    #@-node:tbrown.20090603104805.4963:addToFirstChildButton
    #@+node:tbrown.20090603104805.4964:addToLastChildButton
    def addToLastChildButton (self,event=None):
        self.addButton(first=False)
    #@-node:tbrown.20090603104805.4964:addToLastChildButton
    #@+node:tbrown.20090603104805.4965:addButton
    def addButton(self, class_):

        '''Add a button for an interact class.'''

        c = self.c ; p = c.p
        sc = scriptingController(c)

        mb = InteractButton(c, class_)

        if mb.available():
            b = sc.createIconButton(
                text = mb.interactor.buttonText(),
                command = mb.run,
                shortcut = None,
                statusLine = mb.interactor.statusText(),
                bg = "LightBlue",
            )
    #@-node:tbrown.20090603104805.4965:addButton
    #@-others
#@-node:tbrown.20090603104805.4961:class InteractController
#@+node:tbrown.20090603104805.4966:class InteractButton
class InteractButton:

    """contains target data and function for moving node"""
    #@    @+others
    #@+node:tbrown.20090603104805.4967:__init__

    def __init__(self, c, class_):

        self.c = c
        self.interactor = class_(c)
    #@-node:tbrown.20090603104805.4967:__init__
    #@+node:tbrown.20090603104805.4968:run
    def run(self):

        '''Move the current position to the last child of self.target.'''

        c = self.c
        p = c.p
        self.interactor.run(p)
        c.redraw()
    #@-node:tbrown.20090603104805.4968:run
    #@+node:tbrown.20090603104805.4969:available
    def available(self):
        return self.interactor.available()
    #@-node:tbrown.20090603104805.4969:available
    #@-others
#@-node:tbrown.20090603104805.4966:class InteractButton
#@-others
#@-node:tbrown.20090513125417.5244:@thin interact.py
#@-leo
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.