fexpect 源码

通过fabric 调用执行需要人机交互,输入确认信息时,一般有两种方案:

1、fabric 自身 with settings(prompts=prompt_restore)。
2、pexpect组件解决。

这里的fexpect,其实是对pexpect的包装。只是使用方式上与fabric 配搭起来更加灵活。其主要源码如下:

1、ilogue/fexpect/api.py
####################################
import fabric.api
from ilogue.fexpect.internals import wrapExpectations, wrapExpectationsLocal, ExpectationContext


def expect(promptexpr, response, exitAfter=-1):
    if not exitAfter == -1:
        return [(promptexpr, response, exitAfter)]
    return [(promptexpr, response)]

# Use inside an expect(), like:  `expect('>>>', controlchar('D'))`
def controlchar(char):
    char = char.lower()
    a = ord(char)
    if a >= 97 and a <= 122:
        a = a - ord('a') + 1
        return chr(a)
    d = {'@': 0, '`': 0,
        '[': 27, '{': 27,
        '\\': 28, '|': 28,
        ']': 29, '}': 29,
        '^': 30, '~': 30,
        '_': 31,
        '?': 127}
    if char not in d:
        return 0
    return chr(d[char])

def expecting(e):
    return ExpectationContext(e)

def run(cmd, **kwargs):
    #run wrapper 
    if 'expectations' in fabric.state.env and \
        len(fabric.state.env.expectations) > 0:
        cmd = wrapExpectations(cmd)
    return fabric.api.run(cmd, **kwargs)

def sudo(cmd, **kwargs):
    #sudo wrapper
    if 'expectations' in fabric.state.env and \
        len(fabric.state.env.expectations) > 0:
        cmd = wrapExpectations(cmd)
    return fabric.api.sudo(cmd, **kwargs)

def local(cmd, **kwargs):
    #local wrapper
    if 'expectations' in fabric.state.env and \
        len(fabric.state.env.expectations) > 0:
        cmd = wrapExpectationsLocal(cmd)
    return fabric.api.local(cmd, **kwargs)


2、ilogue/fexpect/internals.py
#####################################
import shortuuid
from StringIO import StringIO
import fabric

class ExpectationContext(object):
    def __init__(self,expectations):
        self.expectations = expectations
    def __enter__(self):
        fabric.state.env.expectations = self.expectations
    def __exit__(self, type, value, tb):
        fabric.state.env.expectations = []

def wrapExpectations(cmd):
    script = createScript(cmd)
    remoteScript = '/tmp/fexpect_'+shortuuid.uuid()
    import pexpect
    pexpect_module = pexpect.__file__
    if pexpect_module.endswith('.pyc'):
        pexpect_module = pexpect_module[:-1]
    # If mode not set explicitly, and this is run as a privileged user, 
    # later command from an unpriviliged user will fail due to the permissions
    # on /tmp/pexpect.py
    fabric.api.put(pexpect_module,'/tmp/', mode=0777) 
    fabric.api.put(StringIO(script),remoteScript)
    wrappedCmd = 'python '+remoteScript
    return wrappedCmd

def wrapExpectationsLocal(cmd):
    script = createScript(cmd)
    remoteScript = '/tmp/fexpect_'+shortuuid.uuid()
    with open(remoteScript, 'w') as filehandle:
        filehandle.write(script)
    wrappedCmd = 'python '+remoteScript
    return wrappedCmd

def createScript(cmd):
    useShell =fabric.state.env.shell
    to = 30*60 # readline timeout 8 hours
    #write header:
    s = '#!/usr/bin/python\n'
    s+= 'import sys\n'
    s+= 'from time import sleep\n'
    s+= 'import pexpect\n'
    #write expectation list:
    s+= 'expectations=['
    for e in fabric.state.env.expectations:
        s+= '"{0}",'.format(e[0])
    s+= ']\n'
    #start
    spwnTem = """child = pexpect.spawn(\"\"\"{shellPrefix}{shell} "{cmd}" \"\"\",timeout={to})\n"""
    s+= spwnTem.format(shell=useShell,cmd=cmd,to=to,shellPrefix=('' if useShell.startswith('/') else '/bin/'))
    s+= "child.logfile = sys.stdout\n"
    s+= "while True:\n"
    s+= "\ttry:\n"
    s+= "\t\ti = child.expect(expectations)\n"
    i = 0
    for e in fabric.state.env.expectations:
        ifkeyw = 'if' if i == 0 else 'elif'
        s+= "\t\t{0} i == {1}:\n".format(ifkeyw,i)
        s+= "\t\t\tchild.sendline('{0}')\n".format(e[1])
        s+= "\t\t\texpectations[i]=\"__MANGLE__\"\n\n"
        if len(e)>2:
            s+= "\t\t\tsleep({0})\n".format(e[2])
            s+= "\t\t\tprint('Exiting fexpect for expected exit.')\n"
            s+= '\t\t\tbreak\n'
        i += 1
    s+= '\texcept pexpect.EOF:\n'
    s+= "\t\tprint('Exiting fexpect for EOF.')\n"
    s+= '\t\tbreak\n'
    s+= '\n'
    return s

 

posted @ 2020-02-25 10:58  老农夫  阅读(397)  评论(0编辑  收藏  举报