Mam długo działający serwer Python i chciałbym móc uaktualnić usługę bez ponownego uruchamiania serwera. Jak najlepiej to zrobić?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Mam długo działający serwer Python i chciałbym móc uaktualnić usługę bez ponownego uruchamiania serwera. Jak najlepiej to zrobić?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Odpowiedzi:
Możesz ponownie załadować moduł, gdy został już zaimportowany za pomocą reload
wbudowanej funkcji (tylko Python 3.4+) :
from importlib import reload
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
W Pythonie 3 reload
został przeniesiony do imp
modułu. W 3.4 imp
został wycofany na korzyść importlib
i reload
został dodany do tego ostatniego. W przypadku kierowania na wersję 3 lub nowszą odwołaj się do odpowiedniego modułu podczas jego wywoływania reload
lub importuj go.
Myślę, że tego właśnie chcesz. Serwery WWW, takie jak serwer programistyczny Django, używają tego, aby można było zobaczyć efekty zmian kodu bez ponownego uruchamiania samego procesu serwera.
Cytując z dokumentów:
Kod modułów Pythona jest ponownie kompilowany, a kod na poziomie modułu ponownie wykonywany, definiując nowy zestaw obiektów, które są powiązane z nazwami w słowniku modułu. Funkcja init modułów rozszerzeń nie jest wywoływana po raz drugi. Podobnie jak w przypadku wszystkich innych obiektów w Pythonie, stare obiekty są odzyskiwane dopiero po tym, jak ich liczba referencji spadnie do zera. Nazwy w przestrzeni nazw modułów są aktualizowane, aby wskazywały na nowe lub zmienione obiekty. Inne odniesienia do starych obiektów (takie jak nazwy zewnętrzne modułu) nie są powiązane w odniesieniu do nowych obiektów i muszą być aktualizowane w każdej przestrzeni nazw, w której występują, jeśli jest to pożądane.
Jak zauważyłeś w swoim pytaniu, będziesz musiał zrekonstruować Foo
obiekty, jeśli Foo
klasa znajduje się w foo
module.
X
nie jest modułem, możeszimport sys; reload(sys.modules[X.__module__])
is_changed
funkcja jest po prostu dowolną funkcją, którą należy napisać; to nie jest wbudowane. Na przykład może otworzyć plik odpowiadający importowanemu modułowi i różnicować go w wersji buforowanej, aby sprawdzić, czy się zmienił.
W Python 3.0–3.3 użyłbyś: imp.reload(module)
BDFL nie odpowiedział na to pytanie.
Jednak imp
został wycofany w wersji 3.4 na rzeczimportlib
(dzięki @Stefan! ).
I myślę więc, że teraz używać importlib.reload(module)
, choć nie jestem pewien.
reload(__builtins__)
obowiązuje w 2.x
Usunięcie modułu może być szczególnie trudne, jeśli nie jest to czysty Python.
Oto kilka informacji z: Jak naprawdę usunąć zaimportowany moduł?
Możesz użyć sys.getrefcount (), aby znaleźć rzeczywistą liczbę referencji.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
Liczby większe niż 3 wskazują, że trudno będzie się pozbyć modułu. Własny moduł „pusty” (nie zawierający niczego) powinien zostać później wyrzucony
>>> del sys.modules["empty"]
>>> del empty
ponieważ trzecie odniesienie jest artefaktem funkcji getrefcount ().
setattr(package, "empty", None)
reload()
ponownie ładuje tylko najwyższy moduł, a wszystko w nim nie zostanie ponownie załadowane, chyba że najpierw usuniesz go z sys.modules.
reload(module)
, ale tylko wtedy, gdy jest całkowicie samodzielny. Jeśli cokolwiek innego ma odniesienie do modułu (lub dowolnego obiektu należącego do modułu), to dostaniesz subtelne i ciekawe błędy spowodowane przez stary kod zawieszający się dłużej niż się spodziewałeś, i rzeczy takie jak isinstance
niedziałanie w różnych wersjach ten sam kod.
Jeśli masz zależności jednokierunkowe, musisz także ponownie załadować wszystkie moduły zależne od ponownie załadowanego modułu, aby pozbyć się wszystkich odniesień do starego kodu. A następnie rekurencyjnie przeładuj moduły zależne od przeładowanych modułów.
Jeśli masz zależności cykliczne, co jest bardzo częste, na przykład podczas przeładowywania pakietu, musisz rozładować wszystkie moduły w grupie za jednym razem. Nie możesz tego zrobić, reload()
ponieważ będzie ponownie importować każdy moduł, zanim jego zależności zostaną odświeżone, pozwalając starym referencjom wkradać się do nowych modułów.
Jedynym sposobem na zrobienie tego w tym przypadku jest włamanie sys.modules
, które jest trochę nieobsługiwane. Musiałbyś przejść i usunąć każdy sys.modules
wpis, który chcesz załadować ponownie przy następnym imporcie, a także usunąć wpisy, których wartości None
dotyczą problemu z implementacją związanego z buforowaniem nieudanych importów względnych. Nie jest to strasznie miłe, ale dopóki masz w pełni samodzielny zestaw zależności, które nie pozostawiają referencji poza bazą kodu, jest to wykonalne.
Prawdopodobnie najlepiej zrestartować serwer. :-)
None
wartościami, ponieważ właśnie wpadam na ten problem: usuwam elementy z sys.modules
i po ponownym zaimportowaniu niektórych importowanych zależności None
.
None
wpisy zdołały wrócić z powrotem przez mechanizm importu po usunięciu „prawdziwych” wpisów, i wydaje mi się, że nie mogę tego zrobić w wersji 2.7; w przyszłości z pewnością nie będzie to już problemem, ponieważ ukryty import względny zniknął. Tymczasem usunięcie wszystkich wpisów z None
wartością wydaje się to naprawić.
reload
funkcję? Jest wbudowany, nie musisz importować żadnej biblioteki.
if 'myModule' in sys.modules:
del sys.modules["myModule"]
nose.run()
, nawet poreload(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]))
i moduł json nadal działa, mimo że nie ma go już w sys.modules.
W przypadku Python 2 użyj wbudowanej funkcji reload () :
reload(module)
W przypadku Python 2 i 3.2–3.3 użyj przeładowania z modułu imp :
import imp
imp.reload(module)
Ale imp
jest przestarzałe od wersji 3.4 na korzyść importlib , więc użyj:
import importlib
importlib.reload(module)
lub
from importlib import reload
reload(module)
from six import reload_module
( pip install six
najpierw trzeba )
Poniższy kod pozwala na kompatybilność z Python 2/3:
try:
reload
except NameError:
# Python 3
from imp import reload
Możesz go używać tak jak reload()
w obu wersjach, co upraszcza sprawę .
Akceptowana odpowiedź nie obsługuje przypadku Y z importu X. Ten kod obsługuje go również i standardowy przypadek importu:
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")
W przypadku przeładowania ponownie przypisujemy nazwy najwyższego poziomu do wartości przechowywanych w nowo załadowanym module, który je aktualizuje.
>>> from X import Y
przeładowaniu wykonaj>>> __import__('X', fromlist='Y')
fromlist='*'
?
from
w instrukcjach importu. Wystarczy surowy import <package>
i jawny pakiet.symbol w kodzie. Zrozum, że nie zawsze jest to możliwe lub pożądane. (Oto jeden wyjątek: z przyszłego importu print_function.)
foo = reload(foo); from foo import *
Jest to nowoczesny sposób ponownego ładowania modułu:
from importlib import reload
Jeśli chcesz obsługiwać wersje Pythona starsze niż 3.5, spróbuj tego:
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+
Aby go użyć, uruchom reload(MODULE)
, zastępując MODULE
moduł, który chcesz ponownie załadować.
Na przykład reload(math)
przeładuje math
moduł.
from importlib import reload
. To możesz zrobić reload(MODULE_NAME)
. Nie ma potrzeby korzystania z tej funkcji.
modulereload(MODULE_NAME)
jest bardziej zrozumiałe niż tylko reload(MODULE_NAME)
i ma mniejszą szansę na konflikt z innymi funkcjami.
Jeśli jesteś nie na serwerze, ale rozwija i trzeba często przeładować moduł, oto miły wskazówka.
Po pierwsze, upewnij się, że używasz doskonałej powłoki IPython z projektu Jupyter Notebook. Po zainstalowaniu Jupytera możesz go uruchomić z ipython
, lub jupyter console
nawet lepiej jupyter qtconsole
, co da ładną kolorową konsolę z uzupełnianiem kodu w dowolnym systemie operacyjnym.
Teraz w swojej powłoce wpisz:
%load_ext autoreload
%autoreload 2
Teraz, każdym razem , gdy uruchomisz skrypt, moduły zostaną ponownie załadowane.
Poza tym 2
istnieją inne opcje magii automatycznego przeładowania :
%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.
Dla tych jak ja, którzy chcą zwolnić wszystkie moduły (gdy są uruchomione w interpretera Pythona pod Emacsem ):
for mod in sys.modules.values():
reload(mod)
Więcej informacji znajduje się w Przeładowywanie modułów Python .
sys.modules.values()
module jest modułem. Na przykład: >>> type (sys.modules.values () [1]) <class 'email.LazyImporter'> Więc jeśli spróbuję uruchomić ten kod, to on się przewraca (wiem, że nie jest to praktyczne rozwiązanie, po prostu wskazując to).
if mod and mod.__name__ != "__main__": imp.reload(mod)
Enthought Traits ma moduł, który działa dość dobrze w tym celu. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
Przeładuje każdy moduł, który został zmieniony, i zaktualizuje inne moduły i obiekty, które go używają. Przez większość czasu nie działa z __very_private__
metodami i może dusić dziedziczenie klas, ale oszczędza mi szalonej ilości czasu od konieczności ponownego uruchamiania aplikacji hosta podczas pisania GUI PyQt lub rzeczy, które działają w programach takich jak Maya lub Nuke. Nie działa to może 20-30% czasu, ale nadal jest niezwykle pomocne.
Pakiet Enthought nie przeładowuje plików w momencie ich zmiany - musisz to nazwać jawnie - ale nie powinno to być takie trudne do wdrożenia, jeśli naprawdę tego potrzebujesz
Ci, którzy używają Pythona 3 i przeładowują z importlib.
Jeśli masz problemy, jak się wydaje, moduł nie ładuje się ponownie ... To dlatego, że potrzebuje trochę czasu, aby ponownie skompilować pyc (do 60 sekund). Piszę tę wskazówkę tylko, że wiesz, czy doświadczyłeś tego rodzaju problemu.
01.02.2018
foo
musi zostać wcześniej pomyślnie zaimportowany. from importlib import reload
, reload(foo)
31,5 importlib - Implementacja importu - dokumentacja Python 3.6.4
Inna opcja. Zobacz, że domyślne ustawienie Python importlib.reload
po prostu ponownie zaimportuje bibliotekę przekazaną jako argument. To nie będzie ponownie załadować bibliotek importowania lib. Jeśli zmieniłeś wiele plików i masz nieco skomplikowany pakiet do zaimportowania, musisz dokonać głębokiego przeładowania .
Jeśli masz zainstalowany IPython lub Jupyter , możesz użyć funkcji do głębokiego przeładowania wszystkich bibliotek:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Jeśli nie masz Jupytera, zainstaluj go za pomocą tego polecenia w powłoce:
pip3 install jupyter
reload() argument must be module
. Korzystam z importu funkcji niestandardowej i wydaje się, że nie działa. Korzystanie z wbudowanych modułów działa. :-( to strata czasu będąc przeładunku ipython dla każdej małej zmiany zrobiłem do mojego kodu ...
Rozwiązanie z przeszłości jest dobre tylko do uzyskania informacji o resecie, ale nie zmieni wszystkich odniesień (więcej niż, reload
ale mniej niż wymagane). Aby faktycznie ustawić wszystkie referencje, musiałem wejść do śmietnika i przepisać tam referencje. Teraz działa jak urok!
Zauważ, że to nie zadziała, jeśli GC jest wyłączony lub jeśli przeładowywanie danych nie jest monitorowane przez GC. Jeśli nie chcesz zadzierać z GC, oryginalna odpowiedź może ci wystarczyć.
Nowy kod:
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)
Jak napisano w odpowiedzi @ bobince, jeśli istnieje już odwołanie do tego modułu w innym module (zwłaszcza jeśli zostało ono zaimportowane przy użyciu as
słowa kluczowego podobnego import numpy as np
), to wystąpienie nie zostanie zastąpione.
Okazało się to dla mnie dość problematyczne, gdy stosowałem testy, które wymagały stanu „czyszczenia” modułów konfiguracyjnych, więc napisałem funkcję o nazwie, reset_module
która korzysta importlib
z reload
funkcji i rekurencyjnie zastępuje wszystkie zadeklarowane atrybuty modułu. Został przetestowany z Pythonem w wersji 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
Uwaga: używaj ostrożnie! Używanie ich w modułach nieperyferyjnych (na przykład modułach, które definiują klasy używane zewnętrznie) może prowadzić do wewnętrznych problemów w Pythonie (takich jak problemy z wytrawianiem / usuwaniem wytrawiania).
dla mnie w przypadku Abaqus jest to sposób, w jaki działa. Wyobraź sobie, że Twój plik to 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'])
Mam problem z przeładowaniem czegoś w Sublime Text, ale w końcu mogłem napisać to narzędzie do przeładowania modułów w Sublime Text na podstawie kodu sublime_plugin.py
używanego do przeładowywania modułów.
Poniżej akceptujesz przeładowanie modułów ze ścieżek ze spacjami na ich nazwach, a następnie po przeładowaniu możesz po prostu zaimportować, jak zwykle.
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()
Jeśli uruchomisz po raz pierwszy, powinno to załadować moduł, ale jeśli później będziesz mógł ponownie zastosować metodę / funkcję run_tests()
, ponownie załaduje pliki testowe. Z Sublime Text ( Python 3.3.6
) zdarza się to często, ponieważ jego tłumacz nigdy się nie zamyka (chyba że uruchomisz ponownie Sublime Text, tj. Python3.3
Interpreter).
Innym sposobem może być zaimportowanie modułu do funkcji. W ten sposób po zakończeniu funkcji moduł zostanie wyrzucony śmieci.
sys.modules
.