原标题:Riposte:使用Python编写的交互式Shell工具
Riposte是一个基于Python的交互式Shell工具。它允许你轻松地将应用程序封装在定制的交互式shell中。关于构建交互式解释器(REPL)的常见繁琐工作已经被考虑到了,因此你可以专注于应用程序的特定域逻辑。
安装
该软件包可在PyPI上使用,因此请使用pip进行安装:
pip install riposte
Riposte支持Python 3.6及更高版本。
使用示例 from riposte import Riposte calculator = Riposte(prompt=”calc:~$ “) MEMORY = [] @calculator.command(“add”) def add(x: int, y: int): result = f”{x} + {y} = {x + y}” MEMORY.append(result) calculator.success(result) @calculator.command(“multiply”) def multiply(x: int, y: int): result = f”{x} * {y} = {x * y}” MEMORY.append(result) calculator.success(result) @calculator.command(“memory”) def memory: for entry in MEMORY: calculator.print(entry) calculator.run calc:~$ add 2 2 [+] 2 + 2 = 4 calc:~$ multiply 3 3 [+] 3 * 3 = 9 calc:~$ memory 2 + 2 = 4 3 * 3 = 9 calc:~$ 命令
首先,你需要注册一些命令以使REPL可操作。可以通过Riposte.command装饰器可以添加命令,并使用处理函数对其进行绑定。
from riposte import Riposte repl = Riposte @repl.command(“hello”) def hello: repl.success(“Is it me you looking for?”) repl.run riposte:~ $ hello [+] Is it me you looking for?
另外Riposte.command接受一些可选参数:
deion 几个描述命令的词,你可以在以后用它来构建有意义的帮助
guides 定义如何解释传递的参数自动补全
Riposte支持命令的Tab键自动补全功能(tab-completion)。你可以以与注册命令类似的方式注册completer函数,只需使用Riposte.complete装饰器,并将其指向特定命令即可。
from riposte import Riposte repl = Riposte START_SUBCOMMANDS = [“foo”, “bar”] @repl.command(“start”) def start(subcommand: str): if subcommand in START_SUBCOMMANDS: repl.status(f”{subcommand} started”) else: repl.error(“Unknown subcommand.”) @repl.complete(“start”) def start_completer(text, line, start_index, end_index): return [ subcommand for subcommand in START_SUBCOMMANDS if subcommand.startswith(text) ] repl.run
补全功能由TAB键触发。每个补全函数都应返回有效选项列表,并接受以下参数:
text 行中的最后一个单词
line 整行的行内容
start_index 该行中最后一个单词的起始索引
end_index 该行中最后一个单词的结束索引
在我们的例子中:
riposte:~ $ start ba text -> “ba” line -> “start ba” start_index -> 6 end_index -> 8
有了这些信息,你可以为每个命令构建自定义的completer函数。
Guides
Guides是一种说明命令应如何解释用户通过提示传递的参数的方法。Riposte依靠类型提示(Type Hints)来做到这一点。
from riposte import Riposte repl = Riposte @repl.command(“guideme”) def guideme(x: int, y: str): repl.print(“x:”, x, type(x)) repl.print(“y:”, y, type(y)) repl.run riposte:~ $ guideme 1 1 x: 1 y: 1
在这两种情况下,我们都将value 1作为x和y传递。基于参数的类型提示,传递的参数在x的情况下被解释为int,在y的情况下被解释为str。你也可以将该技术用于不同的类型。
from riposte import Riposte repl = Riposte @repl.command(“guideme”) def guideme(x: dict, y: list): x[“foo”] = “bar” repl.print(“x:”, x, type(x)) y.append(“foobar”) repl.print(“y:”, y, type(y)) repl.run riposte:~ $ guideme “{‘bar’: ‘baz’}” “[‘barbaz’]” x: {‘bar’: ‘baz’, ‘foo’: ‘bar’} y: [‘barbaz’, ‘foobar’]
另一种更为强大的定义guides用于处理函数参数的方法是,直接从Ricoste.command装饰器定义它。在本例中,以这种方式定义的guide优先于类型提示。
from riposte import Riposte repl = Riposte @repl.command(“guideme”, guides={“x”: [int]}) def guideme(x): repl.print(“x:”, x, type(x)) repl.run riposte:~ $ guideme 1 x: 1
为什么这种方式更加强大?因为通过这种方式可以让你链接不同的guides,其中一个guide的输出是另一个guide的输入,创建验证或将输入转换为更复杂的类型。
from collections import namedtuple from riposte import Riposte from riposte.exceptions import RiposteException from riposte.guides import literal repl = Riposte def non_negative(value: int): if value < 0: raise RiposteException(“Value can’t be negative”) return value Point = namedtuple(“Point”, (“x”, “y”)) def get_point(value: dict): return Point(**value) @repl.command(“guideme”, guides={“x”: [int, non_negative], “y”: [literal, get_point]}) def guideme(x, y): repl.print(“x:”, x, type(x)) repl.print(“y:”, y, type(y)) repl.run riposte:~ $ guideme -1 ‘{“x”: 1, “y”: 2}’ [-] Value can’t be negative riposte:~ $ guideme 1 ‘{“x”: 1, “y”: 2}’ x: 1 y: Point(x=1, y=2) riposte:~ $
这只是一个简单的函数调用,其中输入字符串被传递给链中的第一个引导函数。在这种情况下,调用如下所示:
non_negative(int(“-1”)) # guide chain for parameter `x` get_point(literal(‘{“x”: 1, “y”: 2}’)) # guide chain for parameter `y` 打印
Riposte内置线程安全打印方法:
info
error
status
success
每个方法都遵循Python内置print函数的签名。除了print之外,所有这些都提供与其名称相对应的信息着色( informative coloring)。
我们强烈建议你使用我们的线程安全打印API,但如果你知道自己在做什么,并且100%的确定,那么线程执行在你应用程序生命周期的某个阶段将永远不会出现, 你可以使用Python的内置print函数。
扩展 PrinterMixin
如果要更改现有方法的样式或添加自定义方法,你可以对PrinterMixin类进行扩展。
from riposte import Riposte from riposte.printer.mixins import PrinterMixin class ExtendedPrinterMixin(PrinterMixin): def success(self, *args, **kwargs): # overwriting existing method self.print(*args, **kwargs) def shout(self, *args, **kwargs): # adding new one self.print((*args, “!!!”), **kwargs) class CustomRiposte(Riposte, ExtendedPrinterMixin): pass repl = CustomRiposte @repl.command(“foobar”) def foobar(message: str): repl.shout(message) 自定义 PrinterMixin
对现有的打印API不满意吗?没关系,你也可以使用PrinterBaseMixin及其线程安全_print方法从头开始构建自己的打印API。
from riposte import Riposte from riposte.printer.mixins import PrinterBaseMixin class CustomPrinterMixin(PrinterBaseMixin): def ask(self, *args, **kwargs): # adding new one self._print((*args, “???”), **kwargs) def shout(self, *args, **kwargs): # adding new one self._print((*args, “!!!”), **kwargs) class CustomRiposte(Riposte, CustomPrinterMixin): pass repl = CustomRiposte @repl.command(“foobar”) def foobar(message: str): repl.shout(message) repl.ask(message) repl.success(message) # It’ll raise exception as it’s no longer available 使用 Pallete 对输出着色
如果你想在输出中添加一些颜色,可以使用Pallete。
from riposte import Riposte from riposte.printer import Palette repl = Riposte @repl.command(“foo”) def foo(msg: str): repl.print(Palette.GREEN.format(msg)) # It will be green
Pallete目前支持的颜色如下:
GREY RED GREEN YELLOW BLUE MAGENTA CYAN WHITE BOLD History
命令历史记录存储在.riposte文件的HOME目录中。默认长度为100行。可以使用history_file和history_length参数更改这两个设置。
from pathlib import Path from riposte import Riposte repl = Riposte( history_file=Path.home / “.custom_history_file”, history_length=500, ) Prompt
默认提示符为riposte:~ $你也可以自定义:
from riposte import Riposte repl = Riposte(prompt=”custom-prompt >>> “) repl.run
你还可以通过覆盖Riposte.prompt属性,基于某个对象的状态动态解析提示布局。在以下示例中,我们将根据MODULE值确定prompt:
from riposte import Riposte class Application: def __init__(self): self.module = None class CustomRiposte(Riposte): @property def prompt(self): if app.module: return f”foo:{app.module} > ” else: return self._prompt # reference to `prompt` parameter. app = Application repl = CustomRiposte(prompt=”foo > “) @repl.command(“set”) def set_module(module_name: str): app.module = module_name repl.success(“Module has been set.”) @repl.command(“unset”) def unset_module: app.module = None repl.success(“Module has been unset.”) repl.run foo > set bar [+] Module has been set. foo:bar > unset [+] Module has been unset. foo > Banner # banner.py from riposte import Riposte BANNER = “”” _ _ _ _ _ _ _ _ _ | | | | | | | | | | | | | | | | | |_| | ___| | | ___ | | | | ___ _ __| | __| | | | _ |/ _ \ | |/ _ \ | |/\| |/ _ \| ‘__| |/ _` | | | | | | __/ | | (_) | \ /\ / (_) | | | | (_| |_| \_| |_/\___|_|_|\___/ \/ \/ \___/|_| |_|\__,_(_) Welcome User Hello World v1.2.3 “”” repl = Riposte(banner=BANNER) @repl.command(“hello”) def hello: repl.print(“Hello World!”) repl.run $ python banner.py _ _ _ _ _ _ _ _ _ | | | | | | | | | | | | | | | | | |_| | ___| | | ___ | | | | ___ _ __| | __| | | | _ |/ _ \ | |/ _ \ | |/\| |/ _ \| ‘__| |/ _` | | | | | | __/ | | (_) | \ /\ / (_) | | | | (_| |_| \_| |_/\___|_|_|\___/ \/ \/ \___/|_| |_|\__,_(_) Welcome User Hello World v1.2.3 riposte:~ $ 项目状态
Riposte项目目前正处于开发阶段。未来可能会有一些重大变化,尽管这里出现的很多概念已在routerploit开发过程中经过了实战测试。
致谢
routersploit
click
*参考来源:GitHub,FB小编secist编译,转载请注明来自FreeBuf.COM返回搜狐,查看更多
责任编辑:
今天的文章python编写交互界面怎么用_python需要编译吗分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/88825.html