Ho un server Python di lunga durata e vorrei essere in grado di aggiornare un servizio senza riavviare il server. Qual è il modo migliore per farlo?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Ho un server Python di lunga durata e vorrei essere in grado di aggiornare un servizio senza riavviare il server. Qual è il modo migliore per farlo?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Risposte:
Puoi ricaricare un modulo quando è già stato importato usando la reload
funzione integrata (solo Python 3.4+) :
from importlib import reload
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
In Python 3, è reload
stato spostato nel imp
modulo. In 3.4, è imp
stato deprecato a favore importlib
e è reload
stato aggiunto a quest'ultimo. Quando scegli come target 3 o versioni successive, fai riferimento al modulo appropriato quando lo chiami reload
o lo importi.
Penso che questo sia quello che vuoi. I server Web come il server di sviluppo di Django lo usano in modo da poter vedere gli effetti delle modifiche al codice senza riavviare il processo del server stesso.
Per citare dai documenti:
Il codice dei moduli Python viene ricompilato e il codice a livello di modulo viene rieseguito, definendo un nuovo set di oggetti che sono associati ai nomi nel dizionario del modulo. La funzione init dei moduli di estensione non viene chiamata una seconda volta. Come con tutti gli altri oggetti in Python, i vecchi oggetti vengono recuperati solo dopo che i loro conteggi di riferimento scendono a zero. I nomi nello spazio dei nomi del modulo vengono aggiornati per puntare a qualsiasi oggetto nuovo o modificato. Altri riferimenti ai vecchi oggetti (come nomi esterni al modulo) non vengono rimbalzati per fare riferimento ai nuovi oggetti e devono essere aggiornati in ogni spazio dei nomi in cui si verificano se lo si desidera.
Come hai notato nella tua domanda, dovrai ricostruire gli Foo
oggetti se la Foo
classe risiede nel foo
modulo.
X
non è un modulo, puoiimport sys; reload(sys.modules[X.__module__])
is_changed
funzione sia solo una funzione arbitraria che dovresti scrivere; non è un built-in. Ad esempio, potrebbe eventualmente aprire il file corrispondente al modulo che si sta importando e diffonderlo con una versione cache per vedere se è cambiato.
In Python 3.0–3.3 useresti: imp.reload(module)
Il BDFL ha risposto a questa domanda.
Tuttavia, è imp
stato deprecato in 3.4, a favore diimportlib
(grazie @Stefan! ).
Io credo , quindi, che ci si ora possibile utilizzare importlib.reload(module)
, anche se non sono sicuro.
reload(__builtins__)
è valido allo stesso modo in 2.x
Può essere particolarmente difficile eliminare un modulo se non è Python puro.
Ecco alcune informazioni da: Come posso davvero cancellare un modulo importato?
È possibile utilizzare sys.getrefcount () per scoprire il numero effettivo di riferimenti.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
I numeri maggiori di 3 indicano che sarà difficile liberarsi del modulo. Il modulo "vuoto" (non contenente nulla) nostrano dovrebbe essere raccolto dopo
>>> del sys.modules["empty"]
>>> del empty
come terzo riferimento è un artefatto della funzione getrefcount ().
setattr(package, "empty", None)
reload()
ricarica solo il modulo più in alto e qualsiasi cosa al suo interno non verrà ricaricata a meno che non lo si elimini per la prima volta da sys.modules.
reload(module)
, ma solo se è completamente autonomo. Se qualcos'altro ha un riferimento al modulo (o qualsiasi oggetto appartenente al modulo), otterrai errori sottili e curiosi causati dal vecchio codice in giro più a lungo del previsto e cose come isinstance
non funzionare su diverse versioni del stesso codice.
Se si dispone di dipendenze a senso unico, è inoltre necessario ricaricare tutti i moduli che dipendono dal modulo ricaricato per eliminare tutti i riferimenti al vecchio codice. E quindi ricaricare i moduli che dipendono dai moduli ricaricati, in modo ricorsivo.
Se si hanno dipendenze circolari, cosa molto comune, ad esempio quando si ha a che fare con il ricaricamento di un pacchetto, è necessario scaricare tutti i moduli del gruppo in una volta sola. Non puoi farlo reload()
perché reimporterà ogni modulo prima che le sue dipendenze siano state aggiornate, consentendo ai vecchi riferimenti di insinuarsi in nuovi moduli.
L'unico modo per farlo in questo caso è l'hacking sys.modules
, che è un po 'non supportato. Dovresti passare attraverso ed eliminare ogni sys.modules
voce che desideri ricaricare alla successiva importazione, nonché eliminare le voci i cui valori hanno None
a che fare con un problema di implementazione relativo alla memorizzazione nella cache delle importazioni relative non riuscite. Non è terribilmente bello ma fintanto che hai un set completamente autonomo di dipendenze che non lascia riferimenti al di fuori della sua base di codice, è fattibile.
Probabilmente è meglio riavviare il server. :-)
None
valori perché sto affrontando esattamente questo problema: sto eliminando gli elementi da sys.modules
e dopo la reimportazione di alcune dipendenze importate None
.
None
voci sono riuscite a tornare indietro attraverso il meccanismo di importazione quando le voci "reali" sono state eliminate, e non riesco a farlo accadere su 2.7; in futuro sicuramente non sarà più un problema poiché le importazioni relative implicite sono andate via. Nel frattempo, l'eliminazione di tutte le voci con None
valore sembra risolverlo.
reload
funzione? È integrato, non è necessario importare alcuna libreria.
if 'myModule' in sys.modules:
del sys.modules["myModule"]
nose.run()
, anche doporeload(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]))
e il modulo json funziona ancora anche se non è più nei sys.modules.
Per Python 2 usa la funzione integrata reload () :
reload(module)
Per Python 2 e 3.2–3.3 usare ricaricare dal modulo imp :
import imp
imp.reload(module)
Ma imp
è deprecato dalla versione 3.4 a favore di importlib , quindi usa:
import importlib
importlib.reload(module)
o
from importlib import reload
reload(module)
from six import reload_module
(è necessario pip install six
prima ovviamente)
Il seguente codice consente la compatibilità con Python 2/3:
try:
reload
except NameError:
# Python 3
from imp import reload
Puoi usarlo come reload()
in entrambe le versioni, il che semplifica le cose.
La risposta accettata non gestisce il caso X importato da Y. Questo codice gestisce anche il caso di importazione standard:
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")
Nel caso del ricaricamento, riassegniamo i nomi di livello superiore ai valori memorizzati nel modulo appena ricaricato, che li aggiorna.
>>> from X import Y
ricaricare fare>>> __import__('X', fromlist='Y')
fromlist='*'
?
from
nelle dichiarazioni di importazione. Semplicemente import <package>
esplicito package.symbol nel codice. Renditi conto che questo potrebbe non essere sempre possibile o desiderabile. (Ecco un'eccezione: dal futuro import print_function.)
foo = reload(foo); from foo import *
Questo è il modo moderno di ricaricare un modulo:
from importlib import reload
Se vuoi supportare versioni di Python precedenti alla 3.5, prova questo:
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+
Per usarlo, esegui reload(MODULE)
, sostituendolo MODULE
con il modulo che vuoi ricaricare.
Ad esempio, reload(math)
ricaricherà il math
modulo.
from importlib import reload
. Quindi puoi farlo reload(MODULE_NAME)
. Non è necessario per questa funzione.
modulereload(MODULE_NAME)
sia più autoesplicativo della semplice reload(MODULE_NAME)
e abbia meno possibilità di entrare in conflitto con altre funzioni.
Se si è non è in un server, ma in via di sviluppo e la necessità di frequenti ricaricare un modulo, ecco una bella mancia.
Innanzitutto, assicurati di utilizzare l'eccellente shell IPython , dal progetto Jupyter Notebook. Dopo aver installato Jupyter, puoi avviarlo con ipython
, o jupyter console
, o meglio jupyter qtconsole
, che ti darà una bella console colorata con completamento del codice in qualsiasi sistema operativo.
Ora nella tua shell, digita:
%load_ext autoreload
%autoreload 2
Ora, ogni volta che esegui il tuo script, i tuoi moduli verranno ricaricati.
Oltre a 2
, ci sono altre opzioni della magia di caricamento automatico :
%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.
Per quelli come me che vogliono scaricare tutti i moduli (quando si esegue nell'interprete Python sotto Emacs ):
for mod in sys.modules.values():
reload(mod)
Ulteriori informazioni sono disponibili in Ricaricamento dei moduli Python .
sys.modules.values()
è un modulo. Ad esempio: >>> type (sys.modules.values () [1]) <class 'email.LazyImporter'> Quindi, se provo a eseguire quel codice, cade (so che non è inteso come una soluzione pratica, solo sottolineandolo).
if mod and mod.__name__ != "__main__": imp.reload(mod)
Enthought Traits ha un modulo che funziona abbastanza bene per questo. https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
Ricaricherà qualsiasi modulo che è stato modificato e aggiornerà altri moduli e oggetti istanziati che lo utilizzano. Non funziona il più delle volte con__very_private__
metodi e può soffocare sull'ereditarietà delle classi, ma mi fa risparmiare un sacco di tempo dal dover riavviare l'applicazione host quando si scrivono i guy PyQt o cose che girano all'interno di programmi come Maya o Nuke. Non funziona forse il 20-30% delle volte, ma è comunque incredibilmente utile.
Il pacchetto di Enthought non ricarica i file nel momento in cui cambiano - devi chiamarlo esplicitamente - ma non dovrebbe essere così difficile da implementare se ne hai davvero bisogno
Coloro che utilizzano python 3 e ricaricano da importlib.
Se hai problemi come sembra che il modulo non si ricarichi ... Questo perché ha bisogno di un po 'di tempo per ricompilare pyc (fino a 60 sec). Scrivo questo suggerimento solo per sapere se hai riscontrato questo tipo di problema.
2018/02/01
foo
deve essere importato correttamente in anticipo. from importlib import reload
, reload(foo)
31.5. importlib - L'implementazione di import - documentazione Python 3.6.4
Altra opzione Guarda che Python default importlib.reload
reimporterà semplicemente la libreria passata come argomento. Essa non ricaricare le librerie che il vostro lib importazione. Se hai modificato molti file e hai un pacchetto un po 'complesso da importare, devi eseguire un nuovo caricamento .
Se hai installato IPython o Jupyter , puoi usare una funzione per ricaricare in profondità tutte le librerie:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Se non hai Jupyter, installalo con questo comando nella tua shell:
pip3 install jupyter
reload() argument must be module
. Sto usando un'importazione di funzione personalizzata e non sembra funzionare. L'uso dei moduli integrati funziona. :-( è una perdita di tempo ricaricare iPython per ogni piccola modifica che ho apportato al mio codice ...
La soluzione di prima è buona solo per ottenere le informazioni di ripristino, ma non cambierà tutti i riferimenti (più di reload
ma meno di quanto richiesto). Per impostare effettivamente anche tutti i riferimenti, ho dovuto andare nel Garbage Collector e riscrivere i riferimenti lì. Ora funziona perfettamente!
Si noti che ciò non funzionerà se il GC è spento o se si ricaricano dati non monitorati dal GC. Se non vuoi fare confusione con il GC, la risposta originale potrebbe essere sufficiente per te.
Nuovo codice:
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)
Come scritto nella risposta di @ bobince, se c'è già un riferimento a quel modulo in un altro modulo (specialmente se è stato importato con la as
parola chiave like import numpy as np
), quell'istanza non verrà sovrascritta.
Questo mi è sembrato abbastanza problematico quando ho applicato test che richiedevano uno stato di "clean-slate" dei moduli di configurazione, quindi ho scritto una funzione chiamata reset_module
che usa importlib
la reload
funzione e sovrascrive ricorsivamente tutti gli attributi del modulo dichiarato. È stato testato con Python versione 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
Nota: usare con cura! L'uso di questi su moduli non periferici (moduli che definiscono le classi utilizzate esternamente, ad esempio) potrebbe portare a problemi interni in Python (come problemi di decapaggio / non decapaggio).
per me nel caso di Abaqus è il modo in cui funziona. Immagina che il tuo file sia 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'])
Ho avuto molti problemi a provare a ricaricare qualcosa all'interno di Sublime Text, ma finalmente ho potuto scrivere questa utility per ricaricare i moduli su Sublime Text in base al codice sublime_plugin.py
utilizzato per ricaricare i moduli.
Questo sotto ti accetta di ricaricare i moduli da percorsi con spazi sui loro nomi, quindi in seguito dopo il ricaricamento puoi semplicemente importare come fai di solito.
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()
Se si esegue per la prima volta, questo dovrebbe caricare il modulo, ma se in seguito è possibile ripetere il metodo / funzione run_tests()
ricaricherà i file di test. Con Sublime Text ( Python 3.3.6
) questo accade molto perché il suo interprete non si chiude mai (a meno che non riavvii Sublime Text, cioè l' Python3.3
interprete).
Un altro modo potrebbe essere quello di importare il modulo in una funzione. In questo modo una volta completata la funzione, il modulo riceve la spazzatura raccolta.
sys.modules
.