关于该主题的文档(在撰写本文时)很少。define
如果宏 ( ) 的使用不足以满足我的目的,我该如何扩展 reposurgeon 功能?
它给出的唯一线索是:
该代码可以完全访问所有内部数据结构。以后的eval调用可以访问定义的函数。
但这甚至意味着什么?
我们还了解到:
通常这将是对前一个 exec 定义的函数的调用。变量 _repository 和 _selection 将具有明显的值。请注意,_selection 将是一个整数列表,而不是对象。
关于该主题的文档(在撰写本文时)很少。define
如果宏 ( ) 的使用不足以满足我的目的,我该如何扩展 reposurgeon 功能?
它给出的唯一线索是:
该代码可以完全访问所有内部数据结构。以后的eval调用可以访问定义的函数。
但这甚至意味着什么?
我们还了解到:
通常这将是对前一个 exec 定义的函数的调用。变量 _repository 和 _selection 将具有明显的值。请注意,_selection 将是一个整数列表,而不是对象。
我将使用斜体内联代码 ( like this
) 来表示 Python 代码,并使用“普通”内联代码 ( like this
) 来表示 RepoSurgeon 命令。对于代码块,介绍性描述应提供上下文,即它是 RepoSurgeon 命令还是 Python 代码。
这篇文章讨论了 RepoSurgeon 的 3.10 版本,这是撰写本文时的最新版本。
RepoSurgeon是用 Python 编写的,并明确允许在execfile()
其中使用其他 Python 代码。RepoSurgeon 命令的语法是:
exec </path/to/python-source.py
我们可以从文档中收集到这么多。
我们可以在 Lift 脚本中或 RepoSurgeon 提示符中使用它。
正如本问答中已经指出的那样,在 RepoSurgeon 的上下文中运行时,您需要遵守周围代码强加的规则。特别是您的 Python 代码将在__main__.RepoSurgeon
实例的上下文中执行,所以这是首先要记住的事情。
您还必须始终使用 进行选择eval
。list
不给出任何选择并期望像 for或其他内置命令那样隐含“全部选中”似乎是不合法的,尽管您可以利用exec
来改变这种行为,正如我们稍后会看到的那样。
还要确保使用eval myfunc()
而不是eval myfunc
. 显然myfunc
是一个有效的 Python 语句,但不要指望它会做任何事情。您必须调用该函数。之后的所有内容都eval
直接交给 Python 的eval()
.
在execfile()
(exec
作为 RepoSurgeon)运行时,您可以滥用您正在运行的上下文和引用self
,这是__main__.RepoSurgeon
上面提到的实例。稍后再详细介绍。
考虑以下引入新的未绑定函数的 Python 代码myfunc
:
def myfunc():
print("Hello world!")
并在 RepoSurgeon 提示符下发出以下命令:
exec </path/to/your/python-code.py
其次是:
=O eval myfunc()
这将产生预期的输出:
Hello world!
不过,您可能想使用与我不同的选择。哪个适合您的需求。
注意:在任何情况下,即使是空的选择仍然会导致您的 Python 代码被调用!例如=I
,我加载的 repo 中的选择是空的,但我仍然会看到上面生成的输出。提供任何选择以调用您的代码非常重要。
通过上面的简单示例,我们可以检查它是否有效。现在继续探索我们可以访问的内容_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
代码运行所在的实例(更多信息请参见下文)。
为了查看所有方法等,请在您的函数中使用(或在-ing Python 代码文件dir(self)
时在顶层使用)。exec
所以只需将此行添加到myfunc
:
dir(self)
进行中:
def myfunc():
from pprint import pprint
pprint(globals())
pprint(locals())
dir(self)
再次调用exec
andeval
命令后(在 Linux 上调用它,就像在 shell 中使用 cursor 一样Up)你现在应该看到列出的大部分函数,你也可以找到 RepoSurgeon 代码。
注意:只需重新运行 RepoSurgeon 的exec
命令,然后再运行另一个命令,eval myfunc()
现在将添加__main__.RepoSurgeon
.
虽然到目前为止所有这些都很酷,并且应该让您了解如何在 RepoSurgeon 中运行自己的 Python 代码,但您也可以替换现有__main__.RepoSurgeon
方法。继续阅读。
有了访问权限,self
就可以添加功能和修改现有功能。
RepoSurgeon.precmd
看起来像一个有价值的候选人。它是在运行实际命令之前调用的方法,它执行语法检查以及设置在许多 RepoSurgeon 命令中非常重要的选择集。
我们需要的是原型precmd
。这里是:
def precmd(self, line):
替换方法的技巧又是什么?亚历克斯·马泰利(Alex Martelli)的回答在这里引路...
我们可以简单地将其用作我们的(完整)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):
包含我们的__main__.RepoSurgeon.precmd
. 它添加的令人敬畏的新功能是模仿输入的命令。setattr()
覆盖了。__main__.RepoSurgeon.precmd
RepoSurgeon 的任何后续调用现在都self.precmd()
将通过我们的“钩子”。请记住,我们正在覆盖 RepoSurgeon 的内部代码,因此请谨慎行事,不要做任何愚蠢的事情。该代码非常易读,尽管有高达 10k 的 LoC。
下次你发出任何命令时,你应该把它鹦鹉学舌地回复给你。考虑以下内容(RepoSurgeon 提示加上输出摘录):
reposurgeon% =O list
[pre-precmd] '=O list'
=O list
是我输入的命令和[pre-precmd] '=O list'
它产生的输出(后面是实际输出,因为我__main__.RepoSurgeon.precmd
在我的版本中调用了原始实现)。
RepoSurgeon 命令exec
并eval
提供了一种强大的方法来覆盖现有功能并在 RepoSurgeon 中添加新功能。
eval
钩子示例是通过使用以前的函数“简单地”扩展 RepoSurgeon 的超集exec
。它允许将代码潜入 RepoSurgeon 的内部,并在它有缺点的地方根据我们的意愿弯曲它(到目前为止我只发现了一小部分)。
感谢 ESR 的这一设计决定。这种方式不需要插件框架。