У меня давно работающий сервер Python, и я хотел бы иметь возможность обновлять службу без перезапуска сервера. Какой наилучший способ сделать это?
if foo.py has changed: unimport foo <-- How do I do this? import foo myfoo = foo.Foo()
Переведено автоматически
Ответ 1
Вы можете перезагрузить модуль, когда он уже был импортирован, используя importlib.reload():
from importlib import reload # Python 3.4+ import foo
whileTrue: # Do some things. if is_changed(foo): foo = reload(foo)
В Python 2 reload был встроен. В Python 3 он был перемещен в imp модуль. В версии 3.4 imp был устаревшим в пользу importlib. При настройке версии 3 или более поздней либо ссылайтесь на соответствующий модуль при вызовеreload, либо импортируйте его.
Я думаю, что это то, что вы хотите. Веб-серверы, такие как сервер разработки Django, используют это, чтобы вы могли видеть последствия изменений вашего кода без перезапуска самого серверного процесса.
Цитирую по документам:
Код модуля Python перекомпилируется и код уровня модуля выполняется повторно, определяя новый набор объектов, которые привязаны к именам в словаре модуля, путем повторного использования загрузчика, который изначально загрузил модуль. init Функция модулей расширения не вызывается во второй раз.
Как и в случае со всеми другими объектами в Python, старые объекты восстанавливаются только после того, как количество их ссылок падает до нуля.
Имена в пространстве имен модуля обновляются, чтобы указывать на любые новые или измененные объекты.
Другие ссылки на старые объекты (например, внешние по отношению к модулю имена) не восстанавливаются для ссылки на новые объекты и должны быть обновлены в каждом пространстве имен, где они встречаются, если это необходимо.
Как вы отметили в своем вопросе, вам придется реконструировать Foo объекты, если Foo класс находится в foo модуле.
Числа больше 3 указывают на то, что избавиться от модуля будет сложно . Доморощенный "пустой" (ничего не содержащий) модуль должен быть собран мусор после
>>> del sys.modules["empty"] >>> del empty
в качестве третьей ссылки используется артефакт функции getrefcount().
Ответ 4
reload(module), но только если он полностью автономный. Если что-либо еще имеет ссылку на модуль (или любой объект, принадлежащий модулю), то вы получите тонкие и любопытные ошибки, вызванные тем, что старый код висит дольше, чем вы ожидали, и такие вещи, как isinstance не работает в разных версиях одного и того же кода.
Если у вас односторонние зависимости, вы также должны перезагрузить все модули, которые зависят от перезагруженного модуля, чтобы избавиться от всех ссылок на старый код. А затем рекурсивно перезагрузить модули, которые зависят от перезагруженных модулей.
Если у вас есть циклические зависимости, что очень распространено, например, когда вы имеете дело с перезагрузкой пакета, вы должны выгрузить все модули в группе за один раз. Вы не можете этого сделать с помощью reload(), потому что он повторно импортирует каждый модуль до обновления его зависимостей, позволяя старым ссылкам проникать в новые модули.
Единственный способ сделать это в данном случае - взломать sys.modules, который отчасти не поддерживается. Вам пришлось бы просмотреть и удалить каждую sys.modules запись, которую вы хотели бы перезагрузить при следующем импорте, а также удалить записи, значения которых None чтобы решить проблему реализации, связанную с кэшированием неудачного относительного импорта. Это не очень приятно, но пока у вас есть полностью автономный набор зависимостей, который не оставляет ссылок за пределами своей кодовой базы, это выполнимо.