ソケットモジュールを使用できるようにするには、ソケットモジュールのコピーを作成し、もう1つのソケットモジュールにモンキーパッチを適用して、別の方法で使用する必要があります。
これは可能ですか?
つまり、モジュールを実際にコピーすることを意味します。つまり、実行時に、コピーし、関数をsocketmodule.c
に変更して、拡張機能としてインストールした場合と同じ結果を取得します。initsocket()
initmy_socket()
my_socket
ソケットモジュールを使用できるようにするには、ソケットモジュールのコピーを作成し、もう1つのソケットモジュールにモンキーパッチを適用して、別の方法で使用する必要があります。
これは可能ですか?
つまり、モジュールを実際にコピーすることを意味します。つまり、実行時に、コピーし、関数をsocketmodule.c
に変更して、拡張機能としてインストールした場合と同じ結果を取得します。initsocket()
initmy_socket()
my_socket
モジュールをインポートしてからsys.modulesから削除したり、モジュールをコピーしようとしたりするなど、いつでもトリックを実行できます。ただし、Pythonはすでに標準ライブラリで必要なものを提供しています。
import imp # Standard module to do such things you want to.
# We can import any module including standard ones:
os1=imp.load_module('os1', *imp.find_module('os'))
# Here is another one:
os2=imp.load_module('os2', *imp.find_module('os'))
# This returns True:
id(os1)!=id(os2)
Python3.3 +
imp.load_module
python3.3 +では非推奨であり、importlib
#!/usr/bin/env python3 import sys import importlib.util SPEC_OS = importlib.util.find_spec('os') os1 = importlib.util.module_from_spec(SPEC_OS) SPEC_OS.loader.exec_module(os1) sys.modules['os1'] = os1 os2 = importlib.util.module_from_spec(SPEC_OS) SPEC_OS.loader.exec_module(os2) sys.modules['os2'] = os2 del SPEC_OS assert os1 is not os2, \ "Module `os` instancing failed"
ここでは、同じモジュールを2回インポートしますが、完全に異なるモジュールオブジェクトとしてインポートします。sys.modulesを確認すると、load_module呼び出しの最初のパラメーターとして入力した2つの名前が表示されます。詳細については、ドキュメントをご覧ください。
アップデート:
このアプローチの主な違いを明確にするために、これを明確にしたいと思います。同じモジュールをこの方法でインポートすると、実行時にインポートする他のすべてのモジュールから両方のバージョンにグローバルにアクセスできるようになります。これは、質問者が必要とするものです。わかりました。
以下は、この点を強調する別の例です。
これらの2つのステートメントは、まったく同じことを行います。
import my_socket_module as socket_imported
socket_imported = imp.load_module('my_socket_module',
*imp.find_module('my_socket_module')
)
2行目では、「my_socket_module」文字列を2回繰り返します。これが、importステートメントの動作方法です。しかし、これらの2つの文字列は、実際には2つの異なる理由で使用されます。
find_moduleに渡した2番目のオカレンスは、システムで検出されるファイル名として使用されます。load_moduleメソッドに渡した文字列の最初の出現は、ロードされたモジュールのシステム全体の識別子として使用されます。
したがって、これらには異なる名前を使用できます。つまり、モジュールのpythonソースファイルをコピーしてロードしたのとまったく同じように機能させることができます。
socket = imp.load_module('socket_original', *imp.find_module('my_socket_module'))
socket_monkey = imp.load_module('socket_patched',*imp.find_module('my_socket_module'))
def alternative_implementation(blah, blah):
return 'Happiness'
socket_monkey.original_function = alternative_implementation
import my_sub_module
次に、my_sub_moduleで、システムに存在しない「socket_patched」をインポートできます。ここにmy_sub_module.pyがあります。
import socket_patched
socket_patched.original_function('foo', 'bar')
# This call brings us 'Happiness'
これはかなり嫌ですが、これで十分かもしれません:
import sys
# if socket was already imported, get rid of it and save a copy
save = sys.modules.pop('socket', None)
# import socket again (it's not in sys.modules, so it will be reimported)
import socket as mysock
if save is None:
# if we didn't have a saved copy, remove my version of 'socket'
del sys.modules['socket']
else:
# if we did have a saved copy overwrite my socket with the original
sys.modules['socket'] = save
古いものの関数と変数を使用して新しいモジュールを作成するコードを次に示します。
def copymodule(old):
new = type(old)(old.__name__, old.__doc__)
new.__dict__.update(old.__dict__)
return new
これはモジュールのかなり浅いコピーを行うことに注意してください。辞書は新しく作成されたため、基本的なモンキーパッチは機能しますが、元のモジュールの変更可能なものは2つで共有されます。
編集:コメントによると、深いコピーが必要です。copy
モジュールのディープコピーをサポートするためにモジュールにモンキーパッチを適用しようとしましたが、うまくいきませんでした。次に、モジュールを2回インポートしようとしましたが、モジュールがにキャッシュされsys.modules
ているため、同じモジュールが2回取得されました。最後に、私が思いついた解決策はsys.modules
、最初にモジュールをインポートした後にモジュールを削除してから、再度インポートすることでした。
from imp import find_module, load_module
from sys import modules
def loadtwice(name, path=None):
"""Import two copies of a module.
The name and path arguments are as for `find_module` in the `imp` module.
Note that future imports of the module will return the same object as
the second of the two returned by this function.
"""
startingmods = modules.copy()
foundmod = find_module(name, path)
mod1 = load_module(name, *foundmod)
newmods = set(modules) - set(startingmods)
for m in newmods:
del modules[m]
mod2 = load_module(name, *foundmod)
return mod1, mod2
ソケットモジュールをsocket_monkeyに物理的にコピーして、そこから移動しますか?「賢い」回避策は必要ないと思いますが、単純化しすぎている可能性があります。