The documentation(在撰写本文时)很少。如果使用宏(define
)不足以达到我的目的,我该如何扩展reposurgeon功能呢?
它给出的唯一线索是:
代码可以完全访问所有内部数据结构。定义的函数可供以后的 eval 调用访问。
但这甚至意味着什么?
我们也了解到:
通常,这将是对前一个exec定义的函数的调用。变量_repository和_selection将具有明显的值。请注意,_selection将是整数列表,而不是对象。
答案 0 :(得分:2)
我将使用斜体内联代码( like this
)来表示Python代码和" normal"内联代码(like this
)表示RepoSurgeon命令。对于代码块,介绍性描述应该提供上下文,即它是RepoSurgeon命令还是Python代码。
本文将讨论RepoSurgeon的3.10版本,这是本文撰写时的最新版本。
RepoSurgeon是用Python编写的,并明确允许 execfile()
中的其他Python代码。 RepoSurgeon命令的语法是:
exec </path/to/python-source.py
我们可以从文档中收集到这些内容。
我们可以在电梯脚本或RepoSurgeon提示中使用它。
正如已经指出in this Q&A,你需要在RepoSurgeon的环境中运行时遵守周围代码强加的规则。特别是你的Python代码将在 __main__.RepoSurgeon
实例的上下文中执行,所以这是首先要记住的。
您还必须使用eval
进行选择。不给予任何选择并期望隐含的所有选择&#34;它似乎是合法的。至于list
或其他内置命令,尽管您可以利用exec
来改变这种行为,我们会稍微看一下。
另请确保使用eval myfunc()
而非eval myfunc
。显然 myfunc
是一个有效的Python语句,但不要指望它做任何事情。您必须调用该功能。 eval
之后的所有内容都直接发送到Python eval()
。
当 execfile()
(exec
作为RepoSurgeon)运行时,您可以滥用您正在运行的内容并引用 self
< / em>,这是上面提到的 __main__.RepoSurgeon
的实例。稍后会详细介绍。
考虑以下引入新的未绑定函数的Python代码 myfunc
:
def myfunc():
print("Hello world!")
并在RepoSurgeon提示下发出以下命令:
exec </path/to/your/python-code.py
接下来是:
=O eval myfunc()
这将产生预期的输出:
Hello world!
但是,您可能希望使用与我不同的选择。适合您的需求。
注意:在任何情况下,即使是空的选择仍然会导致您的Python代码被调用!例如,我加载的仓库中的选择=I
为空,但我仍然会看到上面生成的输出。提供任何选择以调用您的代码非常重要。
通过上述简单示例,我们可以检查 是否有效。现在开始探讨除了文档中提到的 _selection
和 _repository
之外我们可以访问的内容。
将功能 myfunc
更改为:
def myfunc():
from pprint import pprint
pprint(globals())
pprint(locals())
应该让我们感受到我们正在处理的事情。
更改后(并保存;))只需重新运行:
exec </path/to/your/python-code.py
接下来是:
=O eval myfunc()
您应该看到globals()
和locals()
的内容转储。
您会注意到,即使在eval
的上下文中,您仍然可以访问 self
( globals()
的一部分在这种情况下)。这非常有用。
正如我之前提到的,您还可以修改运行代码的 __main__.RepoSurgeon
实例(详见下文)。
要查看所有方法等,请在函数中使用 dir(self)
(或者在exec
处使用Python代码文件时在顶层)。
只需将此行添加到 myfunc
:
dir(self)
制作:
def myfunc():
from pprint import pprint
pprint(globals())
pprint(locals())
dir(self)
再次调用exec
和eval
命令后(在Linux上调用它就像在shell中使用游标 Up 那样),您现在应该看到列出的大多数函数您也可以找到RepoSurgeon代码。
注意:只需重新运行RepoSurgeon的exec
命令后跟另一个eval myfunc()
,现在将添加 {{1}属性的输出}}
虽然到目前为止所有这些都很酷,并且应该让您对如何在RepoSurgeon中运行自己的Python代码有所了解,但您也可以替换现有 __main__.RepoSurgeon
方法。请继续阅读。
通过访问 __main__.RepoSurgeon
,可以添加功能和来修改现有功能。
self
看起来像是一个有价值的候选人。它是在运行实际命令之前调用的方法,并执行语法检查以及设置在许多RepoSurgeon命令中非常重要的选择集。
我们需要的是 RepoSurgeon.precmd
的原型。这是:
precmd
替换方法又有什么诀窍? Alex Martelli's answer here引领潮流......
我们可以简单地将其用作def precmd(self, line):
的完整Python文件:
exec
if self:
if not 'orig_precmd' in self.__dict__:
setattr(self, 'orig_precmd', self.precmd) # save original precmd
def myprecmd(self, line):
print("[pre-precmd] '%s'" % line)
orig_precmd = getattr(self, 'orig_precmd')
return self.orig_precmd(line)
setattr(self, 'precmd', myprecmd.__get__(self, self.__class__))
仅用于我们的代码范围。if self:
时不会覆盖此属性的值。exec
包含我们的 myprecmd(self, line):
版本。它添加的令人敬畏的新功能是parrot输入的命令。__main__.RepoSurgeon.precmd
只是用我们的版本覆盖 setattr()
。 RepoSurgeon对 __main__.RepoSurgeon.precmd
所做的任何后续调用都将通过我们的&#34; hook&#34;现在请记住,我们凌驾于RepoSurgeon的内部代码之上,所以要小心谨慎,不要做任何愚蠢的事情。代码非常易读,尽管是10k LoC。
下次你发出任何命令时,你应该把它鹦鹉学舌。请考虑以下内容(RepoSurgeon提示以及输出摘录):
self.precmd()
reposurgeon% =O list
[pre-precmd] '=O list'
是我输入的命令,=O list
输出的输出(后跟实际输出,因为我调用了 [pre-precmd] '=O list'
的原始实现在我的版本中。)
RepoSurgeon命令__main__.RepoSurgeon.precmd
和exec
提供了一种强大的方法来覆盖现有功能并在RepoSurgeon中添加新功能。
钩子示例是&#34;简单&#34;的超集。使用eval
使用之前的eval
&d;功能扩展RepoSurgeon。它允许将代码潜入RepoSurgeon的内部,并将其弯曲到我们的遗嘱,因为它有缺点(到目前为止我只发现了一些)。
对此设计决定感谢ESR。这种方式不需要插件框架。