【问题标题】:Using a coroutine for Python's Cmd class input stream为 Python 的 Cmd 类输入流使用协程
【发布时间】:2019-02-20 12:33:42
【问题描述】:

我面临的问题是:

  • 我有一个异步方法
  • 调用我无法更改的普通 Python 代码
  • 回调一个普通的 Python 方法
  • 需要使用await调用异步代码

我有一个基于Python's Cmd class 构建的自定义命令解释器。我为它提供了自定义标准输入和标准输出。对于这个问题,它看起来像这样:

import cmd
import sys

class CustomStream(object):
    def readline(self):
        return sys.stdin.readline()
    def write(self, msg):
        sys.stdout.write(msg)
    def flush(self):
        pass

class MyShell(cmd.Cmd):
    def do_stuff(self, args):
        print("Getting things done...")
    def do_exit(self, args):
        return True

stream = CustomStream()
shell = MyShell(stdin=stream, stdout=stream)
shell.use_rawinput = False 
shell.cmdloop()

Cmd需要从用户那里读取时,它会这样做:

line = self.stdin.readline()

我想使用基于asyncio 的 AsyncSSH 库为我的自定义解释器提供 SSH 接口。我的 SSH 代码很像 Simple Server sample,它读取类似 stdin 的接口(注意 await 关键字):

line_from_client = (await self._process.stdin.readline()).rstrip('\n')

我尝试了很多方法,但我无法将 SSH 代码输入 Cmd 对标准输入的期望。我必须怎么做才能让我的 CustomStream 对象在内部使用 asyncio/coroutines,同时为 MyShell 提供老式的单线程接口?

【问题讨论】:

标签: python ssh async-await stdin


【解决方案1】:

解决方案是修补 cmdloop 方法以使其能够感知异步。

此代码是 Python 3.7.2 Cmd 类 cmdloop 函数的一个副本,如果你设置则得到

  • raw_input 设置为 True
  • await放在readline前面

此代码中的结果 (aoicmd.py available as a gist):

async def adapter_cmdloop(self, intro=None):
    """Repeatedly issue a prompt, accept input, parse an initial prefix
    off the received input, and dispatch to action methods, passing them
    the remainder of the line as argument.

    """
    self.preloop()

    #This is the same code as the Python 3.7.2 Cmd class, with the
    #following changes
    #  - Remove dead code caused by forcing use_rawinput=False.
    #  - Added a readline in front of readline()
    if intro is not None:
        self.intro = intro
    if self.intro:
        self.stdout.write(str(self.intro)+"\n")
    stop = None
    while not stop:
        if self.cmdqueue:
            line = self.cmdqueue.pop(0)
        else:
            self.stdout.write(self.prompt)
            self.stdout.flush()
            line = await self.stdin.readline()
            if not len(line):
                line = 'EOF'
            else:
                line = line.rstrip('\r\n')
        line = self.precmd(line)
        stop = self.onecmd(line)
        stop = self.postcmd(stop, line)
    self.postloop()

在您需要使用 Cmd 派生类的地方,例如 MyShell,在运行时创建一个名为 MyAsyncShell 的新类:

#Patch the shell with async aware cmdloop
MyAsyncShell = type('MyAsyncSHell', (MyShell,), {
    'cmdloop' :aiocmd.adapter_cmdloop,
    'use_rawinput':False,
})

按照您认为合适的方式实现 writeflush,但您的 readline 应如下所示:

async def readline(self):
    return await my_implementation_of_readline()

【讨论】:

    猜你喜欢
    • 2013-05-12
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多