MonkeyPatching python 子模块

问题描述 投票:0回答:1

我想monkey patch外部代码库来检查同一功能的替代版本。

目前项目树是这样的

.
├── mymodule
│   ├── __init__.py
│   └── myA.py
└── subProjectsDir
    └── subProject
        └── subonemodule
            ├── __init__.py
            └── subtwomodule
                ├── __init__.py
                ├── subA.py
                └── subthreemodule
                    ├── __init__.py
                    └── subB.py

为了更好地在您的计算机上重现该示例,我将项目以 zip 文件形式上传到我的个人云存储空间(此处)。 不过,我会将每个文件的内容保留在这里。

子项目

让我们从我需要修复的子项目开始。我把它放在

subProjectsDir/subProject
目录中。

subonemodule/__init__.py

from . import subtwomodule

subonemodule/subtwomodule/__init__.py

from .subA import funA

subonemodule/subtwomodule/subA.py

def funA():
    print("submoudle subA -> fun A")

这就是我要猴子补丁的功能

subonemodule/subtwomodule/subthreemodule/__init__.py

from .subB import funB

subonemodule/subtwomodule/subthreemodule/subB.py

from ...subtwomodule.subA import funA

def funB():
    print("subsubmoudle subB -> fun B")
    funA()

请注意,这是我的模块调用的函数。此函数依次调用我需要修补的函数。然而,进口链是相对的,这可能会导致问题。

我的代码

mymodule/__init__.py

import os
import sys
thisdir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(thisdir, "..", "subProjectsDir", "subProject"))
print(f"From mymodule launch: {sys.path}")

之前的cose是用来使用我代码里面的子项目的。

mymodule/myA.py

from subonemodule import subtwomodule
from subonemodule.subtwomodule import subA
from subonemodule.subtwomodule.subthreemodule.subB import funB

def myFunA():
    print("My A")

def main():
    # Monkey patch 1, not working
    # subA.funA = myFunA
    # Monkey patch 2, not working
    subtwomodule.subA.funA = myFunA
    funB()

if __name__ == "__main__":
    main()

该文件包含我对函数进行猴子修补的两次尝试

funA
。更清楚地说,目标是每当从属于子项目的任何模块调用函数
funA
时,此类调用都会被对我的函数
myFunA
的调用所取代。 然而,这两种方法都不起作用。

$> python3 -m mymodule.myA
sub3module subB -> fun B
sub2module subA -> fun A

问题

这种情况我该怎么办?我试图不碰所有外部子项目。

python python-3.x monkeypatching
1个回答
0
投票

您需要先修补该函数,然后再由您或您希望覆盖可见的任何其他模块导入该函数。

from subonemodule import subtwomodule

def myFunA():
    print("My A")

# patch funA before any other imports
subtwomodule.subA.funA = myFunA

from subonemodule.subtwomodule.subthreemodule.subB import funB


def main():
    funB()

if __name__ == "__main__":
    main()

说明

打印函数地址可能有助于理解发生了什么。

导入后打补丁

这不起作用

from subonemodule import subtwomodule
from subonemodule.subtwomodule.subthreemodule.subB import funB

def myFunA(): print("My A")

print("original funA", id(subtwomodule.subA.funA))
print("patched  funA", id(myFunA))

# patch funA
subtwomodule.subA.funA = myFunA

print("subB's   funA", id(subtwomodule.subthreemodule.subB.funA))

funB()

输出:

original funA 4339032416
patched  funA 4338922368
subB's   funA 4339032416
sub3module subB -> fun B
sub2module subA -> fun A

sub2module
funA
修补之前导入,因此它导入了原始
funA

导入前打补丁

from subonemodule import subtwomodule

def myFunA():print("My A")

print("original funA", id(subtwomodule.subA.funA))
print("patched  funA", id(myFunA))

# patch before importing
subtwomodule.subA.funA = myFunA
from subonemodule.subtwomodule.subthreemodule.subB import funB

print("subB's   funA", id(subtwomodule.subthreemodule.subB.funA))

funB()

输出:

original funA 4330299744
patched  funA 4330189696
subB's   funA 4330189696
sub3module subB -> fun B
My A

funA
在被
subB
导入之前已被修补,因此当导入 subB 时,它只能看到修补的
funA

© www.soinside.com 2019 - 2024. All rights reserved.