У меня есть долго работающий сервер 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, используют это для того, чтобы вы могли видеть последствия изменений вашего кода без перезапуска самого процесса сервера.
Цитировать из документов:
Код модулей Python перекомпилируется, и код уровня модуля повторно выполняется, определяя новый набор объектов, которые связаны с именами в словаре модуля. Функция init модулей расширения не вызывается во второй раз. Как и со всеми другими объектами в Python, старые объекты восстанавливаются только после того, как их число ссылок упадет до нуля. Имена в пространстве имен модуля обновляются, чтобы указывать на любые новые или измененные объекты. Другие ссылки на старые объекты (например, имена, внешние по отношению к модулю) не привязываются к новым объектам и должны обновляться в каждом пространстве имен, где они встречаются, если это необходимо.
Как вы отметили в своем вопросе, вам придется реконструировать Foo
объекты, если Foo
класс находится в foo
модуле.
is_changed
функция - произвольная функция, которую вы должны написать; это не встроенный. Например, он может открыть файл, соответствующий импортируемому модулю, и сравнить его с кэшированной версией, чтобы увидеть, изменился ли он.
В Python 3.0–3.3 вы бы использовали: imp.reload(module)
BDFL уже ответил на этот вопрос.
Тем не менее, 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
третья ссылка - это артефакт функции getrefcount ().
setattr(package, "empty", None)
reload()
перезагружает только самый верхний модуль, и все, что находится внутри него, не будет перезагружено, если вы сначала не удалите его из sys.modules.
reload(module)
, но только если он полностью автономен. Если что-то еще имеет ссылку на модуль (или какой-либо объект, принадлежащий модулю), то вы получите тонкие и любопытные ошибки, вызванные старым кодом, зависшим дольше, чем вы ожидали, и такими вещами, как isinstance
не работа в разных версиях тот же код
Если у вас есть односторонние зависимости, вы также должны перезагрузить все модули, которые зависят от перезагруженного модуля, чтобы избавиться от всех ссылок на старый код. А затем перезагрузите модули, которые зависят от загруженных модулей, рекурсивно.
Если у вас есть циклические зависимости, которые очень распространены, например, когда вы имеете дело с перезагрузкой пакета, вы должны выгрузить все модули в группе за один раз. Вы не можете сделать это, reload()
потому что он будет повторно импортировать каждый модуль до того, как будут обновлены его зависимости, позволяя старым ссылкам проникать в новые модули.
Единственный способ сделать это в этом случае - взломать sys.modules
, что не поддерживается. Вам нужно будет пройти и удалить каждую sys.modules
запись, которую вы хотите перезагрузить при следующем импорте, а также удалить записи, значения которых относятся None
к проблеме реализации, связанной с кэшированием неудачных относительных импортов. Это не очень приятно, но пока у вас есть полностью автономный набор зависимостей, который не оставляет ссылок вне своей кодовой базы, это работоспособно.
Вероятно, лучше перезагрузить сервер. :-)
None
значениями, потому что я работаю именно с этой проблемой: я удаляю элементы из sys.modules
и после повторного импорта некоторые импортированные зависимости None
.
None
записи смогли вернуться через механизм импорта, когда «реальные» записи были удалены, и я не могу этого добиться в 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 используйте reload из модуля imp :
import imp
imp.reload(module)
Но imp
не рекомендуется с версии 3.4 в пользу importlib , поэтому используйте:
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 <package>
и явный package.symbol в коде. Осознайте, что это не всегда возможно или желательно. (Вот одно исключение: из будущего импорта print_function.)
foo = reload(foo); from foo import *
Это современный способ перезагрузки модуля:
from importlib import reload
Если вы хотите поддерживать версии Python старше 3.5, попробуйте это:
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)
и у него меньше шансов конфликтовать с другими функциями.
Если вы не на сервере, но разрабатываете и вам нужно часто перезагружать модуль, вот хороший совет.
Во-первых, убедитесь, что вы используете превосходную оболочку IPython из проекта Jupyter Notebook. После установки Jupyter вы можете запустить его ipython
, илиjupyter console
, или даже лучше jupyter qtconsole
, что даст вам красивую раскрашенную консоль с дополнением кода в любой ОС.
Теперь в вашей оболочке введите:
%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.
Для таких как я, которые хотят выгрузить все модули (при работе в интерпретаторе Python под Emacs ):
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-приложений или вещи, которые запускаются внутри таких программ, как Maya или Nuke. Это не работает, может быть, 20-30% времени, но все равно невероятно полезно.
Пакет Enthought не перезагружает файлы в тот момент, когда они меняются - вы должны называть это явно - но это не должно быть так сложно реализовать, если вам это действительно нужно
Те, кто использует Python 3 и перезагрузить из importlib.
Если у вас есть проблемы, такие как кажется, что модуль не перезагружается ... Это потому, что ему нужно некоторое время для перекомпиляции pyc (до 60 секунд). Я пишу этот совет только для того, чтобы вы знали, если у вас возникли проблемы такого рода.
2018-02-01
foo
должен быть успешно импортирован заранее. from importlib import reload
, reload(foo)
31.5. importlib - Реализация импорта - документация Python 3.6.4
Другой вариант. Обратите внимание, что Python по умолчанию importlib.reload
просто повторно импортирует библиотеку, переданную в качестве аргумента. Он не перезагрузит библиотеки, которые импортирует ваша библиотека. Если вы изменили много файлов и у вас есть несколько сложный пакет для импорта, вы должны выполнить глубокую перезагрузку .
Если у вас установлен IPython или Jupyter , вы можете использовать функцию для глубокой перезагрузки всех библиотек:
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 Text на основе кода, sublime_plugin.py
используемого для перезагрузки модулей.
Это ниже позволяет вам перезагрузить модули из путей с пробелами в их именах, а затем после перезагрузки вы можете просто импортировать как обычно.
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
интерпретатор).
Другим способом может быть импорт модуля в функцию. Таким образом, когда функция завершает работу, модуль получает мусор.
sys.modules
.