[英]How to replace existing Python class methods of (or otherwise extend) RepoSurgeon by means of 'exec' and 'eval'?
關於該主題的文檔 (在撰寫本文時)很少。 如果使用宏( define
)不足以實現我的目的,該如何擴展后座功能?
它提供的唯一線索是:
該代碼具有對所有內部數據結構的完全訪問權限。 定義的函數可用於以后的eval調用。
但這到底意味着什么?
我們還了解到:
通常,這將是對先前執行程序定義的函數的調用。 變量_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
我們可以從文檔中收集到很多。
我們可以在提升腳本中或RepoSurgeon提示符下使用它。
如本問答中已指出的那樣, 在 RepoSurgeon上下文中運行時,您需要遵守周圍代碼所施加的規則。 特別是您的Python代碼將在__main__.RepoSurgeon
實例的上下文中執行 ,因此這是第一件事。
您還必須始終使用eval
進行選擇。 盡管您可以利用exec
改變行為,但是可以爭辯,但似乎沒有給出任何選擇並期望對list
或其他內置命令隱含“全部選中”的行為是合法的。
還要確保使用eval myfunc()
而不是eval myfunc
。 顯然, myfunc
是有效的Python語句,但是不要期望它會做任何事情。 您必須調用該函數。 eval
之后的所有內容都直接傳遞給Python的eval()
。
當execfile()
( exec
作為RepoSurgeon)運行時,您可以濫用正在運行的上下文並引用self
, self
是上述__main__.RepoSurgeon
的實例。 稍后再詳細介紹。
考慮以下引入了新的未綁定函數myfunc
Python代碼:
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
實例(有關此內容的更多信息,請__main__.RepoSurgeon
下文)。
為了查看所有方法等,請在函數中使用dir(self)
(或在exec
Python代碼文件時在頂層使用)。
因此,只需將此行添加到myfunc
:
dir(self)
進行中:
def myfunc():
from pprint import pprint
pprint(globals())
pprint(locals())
dir(self)
再次調用exec
和eval
命令之后(在Linux上,您可以像使用光標Up一樣在shell中調用它),現在應該看到列出的大多數功能,並且還可以找到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的內部代碼,因此請謹慎行事,不要做任何愚蠢的事情。 該代碼可讀性很強,盡管LoC高達10k。
下次發出任何命令時,都應該讓它重新回到您的手中。 考慮以下內容(RepoSurgeon提示以及輸出摘錄):
reposurgeon% =O list
[pre-precmd] '=O list'
=O list
是我輸入的命令,然后[pre-precmd] '=O list'
產生的輸出(之后是實際輸出,因為我在我的版本中調用__main__.RepoSurgeon.precmd
的原始實現)。
RepoSurgeon命令exec
和eval
提供了一種強大的手段來覆蓋現有功能並在RepoSurgeon中添加新功能。
鈎子示例是通過將eval
與以前的exec
eval
一起使用來“簡單”擴展RepoSurgeon的超集。 它允許將代碼潛入RepoSurgeon的膽量,並在存在缺點的地方將其屈服於我們的意願(到目前為止,我僅發現了少數缺點)。
ESR對此設計決定表示敬意。 不需要這種方式的插件框架。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.