Как разгрузить (перезагрузить) модуль Python?
У меня есть длительно работающий сервер на Python, и я хотел бы иметь возможность обновлять сервис без перезапуска сервера. Какой самый эффективный способ сделать это?
Вот пример кода, который иллюстрирует мою проблему:
if foo.py изменился:
unimport foo # <-- Как это сделать?
import foo
myfoo = foo.Foo()
Как правильно реализовать механизм обновления модуля без остановки всего сервера?
5 ответ(ов)
reload(module)
, но только если модуль полностью автономен. Если на модуль (или на любой объект этого модуля) есть ссылки из других мест, вы столкнетесь с тонкими и странными ошибками, вызванными тем, что старый код будет существовать дольше, чем вы ожидали. В результате, такие операции, как isinstance
, могут не работать с разными версиями одного и того же кода.
Если у вас есть зависимости в одном направлении, вам также необходимо перезагрузить все модули, которые зависят от перезагружаемого модуля, чтобы избавиться от всех ссылок на старый код. После этого нужно рекурсивно перезагрузить модули, которые зависят от только что перезагруженных.
Если у вас есть циклические зависимости, что очень распространено, особенно при работе с перезагрузкой пакетов, вам нужно выгрузить все модули в группе сразу. Использовать reload()
для этого нельзя, так как он будет повторно импортировать каждый модуль, прежде чем его зависимости будут обновлены, что может привести к тому, что старые ссылки проникнут в новые модули.
Единственный способ сделать это в таком случае — манипулировать sys.modules
, что не совсем поддерживается. Вам придется пройти и удалить каждую запись sys.modules
, которую вы хотите перезагрузить при следующем импорте, а также удалить записи, значения которых равны None
, чтобы решить проблему, связанную с кэшированием неудачных относительных импортов. Это не очень приятно, но если у вас есть полностью автономный набор зависимостей, который не оставляет ссылок за пределами своей кодовой базы, этот подход рабочий.
На самом деле, лучше перезапустить сервер. 😃
В Python, если вы хотите удалить модуль из кэша загруженных модулей, вы можете использовать следующий код:
if 'myModule' in sys.modules:
del sys.modules['myModule']
Здесь мы сначала проверяем, загружен ли модуль с именем 'myModule'
в sys.modules
. Если модуль уже загружен, мы удаляем его из этого словаря. Это может быть полезно, например, если вы хотите перезагрузить модуль с помощью importlib.reload()
после внесения изменений в его код. Обратите внимание, что удаление модуля из sys.modules
не удаляет его файлы, а лишь удаляет запись о нем, что позволяет Python заново его загрузить при следующем импорте.
Код, который вы представили, обеспечивает совместимость между Python 2 и 3 для функции reload
. В Python 2 функция reload
доступна как встроенная, а в Python 3 она находится в модуле imp
. Ваш код проверяет, доступна ли функция reload
, и в случае отсутствия (что указывает на использование Python 3) импортирует её из модуля imp
.
Вот перевод кода с пояснением:
try:
reload
except NameError:
# Python 3
from imp import reload
Таким образом, вы можете использовать reload()
в обоих версиях Python, что делает код более универсальным и удобным.
Принятый ответ не учитывает случай импорта с использованием from X import 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)
# используйте вместо: from dfly_parser import parseMessages
importOrReload("dfly_parser", "parseMessages")
В случае перезагрузки мы переназначаем верхнеуровневые имена на значения, хранящиеся в вновь загруженном модуле, что позволяет обновить их.
Современный способ перезагрузки модуля выглядит следующим образом:
from importlib import reload
Если вы хотите поддерживать версии Python старше 3.5, используйте следующий код:
from sys import version_info
if version_info[0] < 3:
pass # В Python 2 встроенная функция 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
, который можно вызывать с модулем, чтобы перезагрузить его. Например, вызов reload(math)
перезагрузит модуль math
.
Импорт модулей из родительской папки
Импорт файлов из другой папки
Как получить путь к модулю?
Относительные импорты в Python 3
Импорт модуля по относительному пути