簡體   English   中英

如何通過“ exec”和“ eval”替換(或擴展)RepoSurgeon的現有Python類方法?

[英]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)運行時,您可以濫用正在運行的上下文並引用selfself是上述__main__.RepoSurgeon的實例。 稍后再詳細介紹。

第一個平凡的例子

考慮以下引入了新的未綁定函數myfunc Python代碼:

def myfunc():
    print("Hello world!")

並在RepoSurgeon提示符下發出以下命令:

exec </path/to/your/python-code.py

其次是:

=O eval myfunc()

這將產生預期的輸出:

Hello world!

不過,您可能要使用與我不同的選擇。 滿足您的需求。

注意:在任何情況下,即使是空選擇也將導致您的Python代碼被調用! 例如,加載的倉庫中的=I選項為空,但仍會看到上面產生的輸出。 它給予援引任何選擇你的代碼簡直是非常重要的。

探索運行Python代碼的上下文

通過上面的例子,我們可以檢查它是否有效。 現在就來探究一下,我們除了可以訪問_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)

再次調用execeval命令之后(在Linux上,您可以像使用光標Up一樣在shell中調用它),現在應該看到列出的大多數功能,並且還可以找到RepoSurgeon代碼。

注意:現在只需重新運行RepoSurgeon的exec命令,然后再運行另一個eval myfunc()現在將添加__main__.RepoSurgeon屬性的輸出。

到目前為止,所有這些都很酷,並且應該使您對如何在RepoSurgeon中運行自己的Python代碼有一種感覺,但是您也可以替換現有的__main__.RepoSurgeon方法。 繼續閱讀。

接觸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:僅僅是為了限制我們的代碼。
  • 檢查“ orig_precmd”可確保我們在隨后調用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命令execeval提供了一種強大的手段來覆蓋現有功能並在RepoSurgeon中添加新功能。

鈎子示例是通過將eval與以前的exec eval一起使用來“簡單”擴展RepoSurgeon的超集。 它允許將代碼潛入RepoSurgeon的膽量,並在存在缺點的地方將其屈服於我們的意願(到目前為止,我僅發現了少數缺點)。

ESR對此設計決定表示敬意。 不需要這種方式的插件框架。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM