実行時間の長いPythonサーバーがあり、サーバーを再起動せずにサービスをアップグレードできるようにしたいと考えています。これを行う最良の方法は何ですか?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
実行時間の長いPythonサーバーがあり、サーバーを再起動せずにサービスをアップグレードできるようにしたいと考えています。これを行う最良の方法は何ですか?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
回答:
reload
組み込み関数(Python 3.4以降のみ)を使用して、すでにインポートされているモジュールをリロードできます。
from importlib import reload
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
Python 3ではreload
、imp
モジュールに移動されました。3.4、imp
を支持して非難しimportlib
、そしてreload
後者に添加しました。3以降をターゲットとする場合は、呼び出し時に適切なモジュールを参照するreload
か、インポートします。
これがあなたの望んでいることだと思います。Djangoの開発サーバーなどのWebサーバーはこれを使用しているため、サーバープロセス自体を再起動せずにコード変更の影響を確認できます。
ドキュメントから引用するには:
Pythonモジュールのコードが再コンパイルされ、モジュールレベルのコードが再実行されて、モジュールのディクショナリの名前にバインドされる新しいオブジェクトのセットが定義されます。拡張モジュールのinit関数は、2回目は呼び出されません。Pythonの他のすべてのオブジェクトと同様に、古いオブジェクトは、参照カウントがゼロになって初めて回収されます。モジュール名前空間の名前は、新しいオブジェクトまたは変更されたオブジェクトを指すように更新されます。古いオブジェクトへのその他の参照(モジュールの外部の名前など)は、新しいオブジェクトを参照するために再バインドされず、必要に応じて、それらが発生する各ネームスペースで更新する必要があります。
質問で述べたFoo
ように、Foo
クラスがfoo
モジュール内にある場合は、オブジェクトを再構築する必要があります。
X
がモジュールでない場合は、次のことができますimport sys; reload(sys.modules[X.__module__])
is_changed
関数があなたが書かなければならないだろう任意の関数だとかなり確信しています。組み込みではありません。たとえば、インポートするモジュールに対応するファイルを開き、キャッシュされたバージョンと比較して、変更されているかどうかを確認することができます。
Python 3.0–3.3では、以下を使用します。 imp.reload(module)
ただし、imp
3.4で非推奨となり、importlib
(@ Stefanに感謝!)
私はと思うので、あなたが今使用すると思い、importlib.reload(module)
私はわからないが、。
reload(__builtins__)
2.x でも同じです
純粋なPythonでないモジュールを削除することは特に困難です。
:ここからいくつかの情報である、私は本当にインポートされたモジュールを削除するにはどうすればよいですか?
sys.getrefcount()を使用して、実際の参照数を確認できます。
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
3より大きい数値は、モジュールを取り除くのが難しいことを示します。自作の「空」(何も含まない)モジュールは、後にガベージコレクションされます。
>>> del sys.modules["empty"]
>>> del empty
3番目の参照はgetrefcount()関数のアーティファクトであるためです。
setattr(package, "empty", None)
reload()
最上位のモジュールのみを再ロードします。最初にsys.modulesから削除しない限り、モジュール内のすべては再ロードされません。
reload(module)
、ただしそれが完全にスタンドアロンの場合のみ。他に何かがモジュール(またはモジュールに属するオブジェクト)への参照を持っている場合、古いコードが予想よりも長くぶらつくことによって引き起こされる、微妙で奇妙なエラーが発生します。isinstance
、異なるバージョンの間で動作していません同じコード。
一方向の依存関係がある場合は、再ロードされたモジュールに依存するすべてのモジュールを再ロードして、古いコードへのすべての参照を取り除く必要があります。そして、再ロードされたモジュールに依存するモジュールを再帰的に再ロードします。
循環依存関係がある場合(パッケージのリロードを処理する場合など)は非常に一般的ですが、グループ内のすべてのモジュールを一度にアンロードする必要があります。reload()
依存関係が更新される前に各モジュールを再インポートし、古い参照が新しいモジュールに侵入できるため、これを行うことはできません。
この場合、これを行う唯一の方法は、ハッキングsys.modules
することです。sys.modules
次のインポートでリロードしたい各エントリを調べて削除し、値が次のエントリも削除する必要がありますNone
。また、失敗した相対インポートのキャッシュに関する実装の問題に対処。それはひどく良くありませんが、コードベースの外に参照を残さない完全に独立した依存関係のセットがある限り、それは実行可能です。
サーバーを再起動することをお勧めします。:-)
None
私はこの問題に正確に直面しているので、パーツを値で詳しく説明できますか?sys.modules
インポートされた依存関係からアイテムを削除し、インポートした依存関係の一部を削除しますNone
。
None
「実際の」エントリが削除されたときに、エントリがインポートメカニズムを介してどのように戻ってきたのか(import.cコードを見ても)は私にはわかりません。2.7 でそれを実行させることはできません。将来的には、暗黙的な相対インポートがなくなったため、問題はなくなりました。それまでの間、None
値を持つすべてのエントリを削除すると、問題が解決するようです。
reload
関数のことですか?これは組み込みで、ライブラリをインポートする必要はありません。
if 'myModule' in sys.modules:
del sys.modules["myModule"]
nose.run()
後、呼び出した後も古い名前が残りましたreload(my_module)
%run my_module
[del(sys.modules[mod] for mod in sys.modules.keys() if mod.startswith('myModule.')]
。
import sys; import json; del sys.modules['json']; print(json.dumps([1]))
jsonモジュールは、sys.modulesに含まれていなくても機能しています。
Python 2の場合は、組み込み関数reload()を使用します。
reload(module)
Python 2および3.2–3.3の場合、impモジュールからreloadを使用します。
import imp
imp.reload(module)
ただし、バージョン3.4以降ではimportlib imp
が推奨されているため、以下を使用してください。
import importlib
importlib.reload(module)
または
from importlib import reload
reload(module)
from six import reload_module
必要ですpip install six
)
次のコードでは、Python 2/3との互換性を確保しています。
try:
reload
except NameError:
# Python 3
from imp import reload
reload()
両方のバージョンと同じように使用できるので、物事が簡単になります。
受け入れられた回答は、XからのインポートYのケースを処理しません。このコードは、それと標準のインポートの場合も処理します。
def importOrReload(module_name, *names):
import sys
if module_name in sys.modules:
reload(sys.modules[module_name])
else:
__import__(module_name, fromlist=names)
for name in names:
globals()[name] = getattr(sys.modules[module_name], name)
# use instead of: from dfly_parser import parseMessages
importOrReload("dfly_parser", "parseMessages")
リロードの場合は、新しくリロードされたモジュールに格納されている値に最上位の名前を再割り当てし、それらを更新します。
>>> from X import Y
、再読み込みしてから>>> __import__('X', fromlist='Y')
fromlist='*'
ますか?
from
importステートメントでのほとんどすべての使用をやめる傾向にあります。ただ、荒涼import <package>
とコードで明示的にpackage.symbol。これが常に可能または望ましいとは限らないことを認識してください。(1つの例外は次のとおりです。将来のインポートprint_functionから。)
foo = reload(foo); from foo import *
これはモジュールをリロードする最新の方法です:
from importlib import reload
3.5より古いバージョンのPythonをサポートしたい場合は、次のことを試してください。
from sys import version_info
if version_info[0] < 3:
pass # Python 2 has built in reload
elif version_info[0] == 3 and version_info[1] <= 4:
from imp import reload # Python 3.0 - 3.4
else:
from importlib import reload # Python 3.5+
これを使用するには、を実行しreload(MODULE)
、MODULE
リロードするモジュールに置き換えます。
たとえばreload(math)
、math
モジュールをリロードします。
from importlib import reload
。その後、行うことができますreload(MODULE_NAME)
。この機能は必要ありません。
modulereload(MODULE_NAME)
、単に説明するだけでなくreload(MODULE_NAME)
、他の機能と競合する可能性が低いと考えています。
サーバーを使用していないが、開発中にモジュールを頻繁にリロードする必要がある場合は、ここにヒントがあります。
まず、Jupyter Notebookプロジェクトの優れたIPythonシェルを使用していることを確認します。Jupyterをインストールした後、あなたがそれを起動することができipython
、またはjupyter console
より良い、または、jupyter qtconsole
起動できます。これにより、どのOSでもコード補完機能を備えた、色分けされた素敵なコンソールが得られます。
シェルで次のように入力します。
%load_ext autoreload
%autoreload 2
今、毎回スクリプトを実行するに、モジュールがリロードされます。
以外2
にも、自動リロードマジックには他のオプションがあります。
%autoreload
Reload all modules (except those excluded by %aimport) automatically now.
%autoreload 0
Disable automatic reloading.
%autoreload 1
Reload all modules imported with %aimport every time before executing the Python code typed.
%autoreload 2
Reload all modules (except those excluded by %aimport) every time before
executing the Python code typed.
私のようにすべてのモジュールをアンロードしたい場合(Emacsの下でPythonインタープリターで実行している場合):
for mod in sys.modules.values():
reload(mod)
詳細については、「Pythonモジュールの再読み込み」を参照してください。
sys.modules.values()
。次に例を示します。>>> type(sys.modules.values()[1])<class 'email.LazyImporter'>したがって、そのコードを実行しようとすると、コードはフォールオーバーします(実用的な解決策ではないことを知っています。指摘)。
if mod and mod.__name__ != "__main__": imp.reload(mod)
Enthought Traitsには、このためにかなりうまく機能するモジュールがあります。https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
変更されたモジュールをリロードし、それを使用している他のモジュールとインスタンス化されたオブジェクトを更新します。ほとんどの場合、__very_private__
メソッドでは機能せず、クラスの継承を阻害する可能性がありますが、PyQtのGUIや、MayaやNukeなどのプログラム内で実行されるものを作成するときにホストアプリケーションを再起動する必要がなくなり、時間を大幅に節約できます。20-30%の確率では機能しませんが、それでも非常に役立ちます。
Enthoughtのパッケージは、ファイルが変更されたときにファイルをリロードしません。明示的に呼び出す必要がありますが、本当に必要な場合でも、実装するのはそれほど難しくありません。
2018-02-01
foo
を事前に正常にインポートする必要があります。 from importlib import reload
、 reload(foo)
その他のオプション。Pythonのデフォルトでimportlib.reload
は、引数として渡されたライブラリが再インポートされるだけです。それはないだろう、あなたのlibにインポートというライブラリをリロードします。多くのファイルを変更し、インポートするパッケージが多少複雑な場合は、ディープリロードを実行する必要があります。
あなたが持っている場合はIPythonまたはJupyterがインストールされ、あなたは深いリロードすべてのLIBSの機能を使用することができます。
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Jupyterがない場合は、シェルで次のコマンドを使用してインストールします。
pip3 install jupyter
reload() argument must be module
ます。カスタム関数インポートを使用していますが、機能していないようです。組み込みモジュールを使用しても機能します。:-(私がコードに加えた小さな変更ごとにiPythonをリロードするのは時間の無駄です...
以前からの解決策は、リセット情報を取得するだけの場合に適していますが、すべての参照を変更するわけではありません(reload
必要以上の変更はありません)。実際にすべての参照も設定するには、ガベージコレクターに移動し、そこで参照を書き換える必要がありました。今それは魅力のように機能します!
これは、GCがオフになっている場合、またはGCによって監視されていないデータを再ロードする場合には機能しないことに注意してください。GCを台無しにしたくない場合は、元の答えで十分かもしれません。
新しいコード:
import importlib
import inspect
import gc
from weakref import ref
def reset_module(module, inner_modules_also=True):
"""
This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
to be the reloaded-module's
:param module: The module to reload (module reference, not the name)
:param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
"""
# For the case when the module is actually a package
if inner_modules_also:
submods = {submod for _, submod in inspect.getmembers(module)
if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
for submod in submods:
reset_module(submod, True)
# First, log all the references before reloading (because some references may be changed by the reload operation).
module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)
new_module = importlib.reload(module)
_reset_item_recursively(module, module_tree, new_module)
def _update_referrers(item, new_item):
refs = gc.get_referrers(item)
weak_ref_item = ref(item)
for coll in refs:
if type(coll) == dict:
enumerator = coll.keys()
elif type(coll) == list:
enumerator = range(len(coll))
else:
continue
for key in enumerator:
if weak_ref_item() is None:
# No refs are left in the GC
return
if coll[key] is weak_ref_item():
coll[key] = new_item
def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
if grayed_out_item_ids is None:
grayed_out_item_ids = set()
item_tree = dict()
attr_names = set(dir(item)) - _readonly_attrs
for sub_item_name in attr_names:
sub_item = getattr(item, sub_item_name)
item_tree[sub_item_name] = [sub_item, None]
try:
# Will work for classes and functions defined in that module.
mod_name = sub_item.__module__
except AttributeError:
mod_name = None
# If this item was defined within this module, deep-reset
if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
or isinstance(sub_item, EnumMeta):
continue
grayed_out_item_ids.add(id(sub_item))
item_tree[sub_item_name][1] = \
_get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)
return item_tree
def _reset_item_recursively(item, item_subtree, new_item):
# Set children first so we don't lose the current references.
if item_subtree is not None:
for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():
try:
new_sub_item = getattr(new_item, sub_item_name)
except AttributeError:
# The item doesn't exist in the reloaded module. Ignore.
continue
try:
# Set the item
_reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
except Exception as ex:
pass
_update_referrers(item, new_item)
@bobinceの回答に記載されているように、別のモジュールにそのモジュールへの参照がすでにある場合(特に、as
キーワードlikeでインポートされた場合import numpy as np
)、そのインスタンスは上書きされません。
これは、構成モジュールの「白紙状態」を必要とするテストを適用するときに非常に問題があることがわかったため、の関数reset_module
を使用し、宣言されたモジュールのすべての属性を再帰的に上書きするという名前importlib
のreload
関数を記述しました。Pythonバージョン3.6でテストされています。
import importlib
import inspect
from enum import EnumMeta
_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
'__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
'__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
'__basicsize__', '__base__'}
def reset_module(module, inner_modules_also=True):
"""
This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
to be the reloaded-module's
:param module: The module to reload (module reference, not the name)
:param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
"""
new_module = importlib.reload(module)
reset_items = set()
# For the case when the module is actually a package
if inner_modules_also:
submods = {submod for _, submod in inspect.getmembers(module)
if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
for submod in submods:
reset_module(submod, True)
_reset_item_recursively(module, new_module, module.__name__, reset_items)
def _reset_item_recursively(item, new_item, module_name, reset_items=None):
if reset_items is None:
reset_items = set()
attr_names = set(dir(item)) - _readonly_attrs
for sitem_name in attr_names:
sitem = getattr(item, sitem_name)
new_sitem = getattr(new_item, sitem_name)
try:
# Set the item
setattr(item, sitem_name, new_sitem)
try:
# Will work for classes and functions defined in that module.
mod_name = sitem.__module__
except AttributeError:
mod_name = None
# If this item was defined within this module, deep-reset
if (mod_name is None) or (mod_name != module_name) or (id(sitem) in reset_items) \
or isinstance(sitem, EnumMeta): # Deal with enums
continue
reset_items.add(id(sitem))
_reset_item_recursively(sitem, new_sitem, module_name, reset_items)
except Exception as ex:
raise Exception(sitem_name) from ex
注:注意して使用してください!これらを非周辺モジュール(たとえば、外部で使用されるクラスを定義するモジュール)で使用すると、Pythonの内部問題(酸洗い/酸洗いの問題など)につながる可能性があります。
私にとってAbaqusの場合は、それが機能する方法です。あなたのファイルがClass_VerticesEdges.pyであると想像してください
sys.path.append('D:\...\My Pythons')
if 'Class_VerticesEdges' in sys.modules:
del sys.modules['Class_VerticesEdges']
print 'old module Class_VerticesEdges deleted'
from Class_VerticesEdges import *
reload(sys.modules['Class_VerticesEdges'])
Sublime Text内で何かを再読み込みしようとすると、多くの問題が発生しましたが、最終的に、このユーティリティを記述sublime_plugin.py
して、モジュールの再読み込みに使用するコードに基づいてSublime Textにモジュールを再読み込みすることができました。
以下では、名前にスペースが含まれているパスからモジュールをリロードすることを許可しています。その後、リロードした後、通常どおりにインポートすることができます。
def reload_module(full_module_name):
"""
Assuming the folder `full_module_name` is a folder inside some
folder on the python sys.path, for example, sys.path as `C:/`, and
you are inside the folder `C:/Path With Spaces` on the file
`C:/Path With Spaces/main.py` and want to re-import some files on
the folder `C:/Path With Spaces/tests`
@param full_module_name the relative full path to the module file
you want to reload from a folder on the
python `sys.path`
"""
import imp
import sys
import importlib
if full_module_name in sys.modules:
module_object = sys.modules[full_module_name]
module_object = imp.reload( module_object )
else:
importlib.import_module( full_module_name )
def run_tests():
print( "\n\n" )
reload_module( "Path With Spaces.tests.semantic_linefeed_unit_tests" )
reload_module( "Path With Spaces.tests.semantic_linefeed_manual_tests" )
from .tests import semantic_linefeed_unit_tests
from .tests import semantic_linefeed_manual_tests
semantic_linefeed_unit_tests.run_unit_tests()
semantic_linefeed_manual_tests.run_manual_tests()
if __name__ == "__main__":
run_tests()
初めて実行する場合、これによりモジュールがロードされますが、後でメソッド/関数を再度実行できる場合run_tests()
は、テストファイルを再ロードします。Sublime Text(Python 3.3.6
)では、インタプリタが閉じないため(Sublime Text、つまりPython3.3
インタプリタを再起動しない限り)、これはよく起こります。
もう1つの方法は、モジュールを関数にインポートすることです。このように、関数が完了すると、モジュールはガベージコレクションされます。
sys.modules
。