У мене давно працює сервер 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
модулі.
X
не модуль, можнаimport sys; reload(sys.modules[X.__module__])
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 використовуйте перезавантаження з модуля 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()
є модулем. Наприклад: >>> введіть (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 guis або матеріалів, які виконуються в таких програмах, як 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
просто повторно імпортує бібліотеку, передану як аргумент. Він не перезавантажить бібліотеки, які імпортує ваш lib. Якщо ви змінили багато файлів і маєте трохи складний пакет для імпорту, ви повинні зробити глибоке перезавантаження .
Якщо у вас встановлено IPython або Jupyter , ви можете використовувати функцію для глибокого перезавантаження всіх ліб:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Якщо у вас немає Юпітера, встановіть його за допомогою цієї команди у своїй оболонці:
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
ключовим словом типу import numpy as np
), цей екземпляр не буде перезаписаний.
Це виявилося вельми проблематично для мене , коли застосування тестів , які вимагають «чистого аркуша» стан модулів конфігурації, тому я написав функцію з ім'ям , reset_module
який використовує importlib
«и reload
функції і рекурсивно перезаписує всі атрибути Заявлена модуля. Він був протестований з версією 3.6 Python.
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 (таких як проблеми з підбиранням / відкочування).
для мене для випадку Абаку це так, як він працює. Уявіть, що ваш файл - 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
.