Ik heb een langlopende Python-server en wil graag een service upgraden zonder de server opnieuw te starten. Wat is de beste manier om dit te doen?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
1, Autoriteit 100%
U kunt een module opnieuw laden wanneer deze al is geïmporteerd door gebruik te maken van de reload
ingebouwde functie (alleen 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
werd verplaatst naar de imp
module. In 3.4 werd imp
verouderd ten gunste van importlib
en reload
werd toegevoegd aan de laatste. Bij het richten van 3 of later, verwijs ofwel de juiste module bij het bellen van reload
of importeer deze.
Ik denk dat dit is wat je wilt. Webservers zoals Django’s Development Server gebruiken dit, zodat u de effecten van uw codewijzigingen kunt zien zonder het serverproces zelf opnieuw te starten.
Offerte van de Documenten:
Code van Python Modules wordt opnieuw gecompileerd en
de module-niveau-code is opnieuw uitgevoerd,
Een nieuwe reeks objecten definiëren die
zijn gebonden aan namen in de module
woordenboek. De init-functie van
uitbreidingsmodules heet niet a
tweede keer. Zoals bij alle andere objecten
in Python zijn de oude objecten alleen
teruggewonnen na hun referentietellingen
naar nul dalen. De namen in de module
naamruimte worden bijgewerkt om te verwijzen naar een
nieuwe of gewijzigde objecten. Ander
verwijzingen naar de oude objecten (zoals
namen buiten de module) zijn niet
rebound om te verwijzen naar de nieuwe objecten
en moet in elke naamruimte worden bijgewerkt
waar ze voorkomen als dat gewenst is.
Zoals je in je vraag hebt opgemerkt, moet je foo
-objecten reconstrueren als de klasse foo
zich in de module foo
bevindt.
Antwoord 2, autoriteit 28%
In Python 3.0–3.3 zou je het volgende gebruiken: imp.reload(module)
De BDFLheeft beantwoorddedeze vraag.
Echter, imp
is in 3.4 afgeschaft ten gunste van importlib
(bedankt @Stefan!).
Ik denkdaarom dat u nu importlib.reload(module)
, hoewel ik het niet zeker weet.
Antwoord 3, autoriteit 12%
Het kan bijzonder moeilijk zijn om een module te verwijderen als het geen pure Python is.
Hier is wat informatie van: Hoe verwijder ik een geïmporteerde module echt?
U kunt sys.getrefcount() gebruiken om het werkelijke aantal . te achterhalen
referenties.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
Getallen groter dan 3 geven aan dat
het zal moeilijk zijn om zich te ontdoen van de
module. De inlandse “lege”
(die niets bevat) module zou moeten zijn
afval verzameld na
>>> del sys.modules["empty"]
>>> del empty
omdat de derde referentie een artefact is
van de functie getrefcount().
Antwoord 4, autoriteit 9%
reload(module)
, maar alleen als het volledig op zichzelf staat. Als iets anders een verwijzing heeft naar de module (of een object dat bij de module hoort), dan krijg je subtiele en merkwaardige fouten die worden veroorzaakt doordat de oude code langer blijft hangen dan je had verwacht, en dingen als isinstance
werkt niet in verschillende versies van dezelfde code.
Als je afhankelijkheden in één richting hebt, moet je ook alle modules die afhankelijk zijn van de opnieuw geladen module opnieuw laden om alle verwijzingen naar de oude code te verwijderen. En herlaad vervolgens modules die afhankelijk zijn van de opnieuw geladen modules, recursief.
Als je circulaire afhankelijkheden hebt, wat bijvoorbeeld heel gebruikelijk is als je een pakket opnieuw moet laden, moet je alle modules in de groep in één keer ontladen. Je kunt dit niet doen met reload()
omdat het elke module opnieuw zal importeren voordat de afhankelijkheden zijn vernieuwd, waardoor oude verwijzingen in nieuwe modules kunnen kruipen.
De enige manier om dit in dit geval te doen, is door sys.modules
te hacken, wat een beetje niet wordt ondersteund. U moet elk sys.modules
-item doorlopen en verwijderen dat u bij de volgende import opnieuw wilt laden, en ook items verwijderen waarvan de waarden None
zijn om een implementatie af te handelen probleem met het cachen van mislukte relatieve importen. Het is niet erg leuk, maar zolang je een volledig op zichzelf staande set afhankelijkheden hebt die geen referenties buiten de codebase achterlaat, is het werkbaar.
Het is waarschijnlijk het beste om de server opnieuw op te starten. 🙂
Antwoord 5, autoriteit 8%
Gebruik voor Python 2de ingebouwde functie reload
:
reload(module)
Voor Python 2enPython 3.2—3.3gebruik reload
van module imp:
import imp
imp.reload(module)
Voor Python ≥3.4, imp
is afgeschaftten gunste van importlib
, dus gebruik dit:
import importlib
importlib.reload(module)
of:
from importlib import reload
reload(module)
TL;DR:
Python ≥ 3.4: importlib.reload(module)
Python 3.2 – 3.3: imp.reload(module)
Python 2: reload(module)
6, Autoriteit 7%
if 'myModule' in sys.modules:
del sys.modules["myModule"]
7, Autoriteit 3%
De volgende code Hiermee kunt u Python 2/3-compatibiliteit:
try:
reload
except NameError:
# Python 3
from imp import reload
Hiermee kunt u het gebruiken als reload()
in beide versies die de dingen eenvoudiger maken.
8, Autoriteit 2%
Het geaccepteerde antwoord verwerkt niet van X-import Y-zaak. Deze code behandelt het en de standaard invoercase ook:
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")
In de herlaadbehuizing halen we de namen van topniveau opnieuw toe aan de waarden die zijn opgeslagen in de nieuw reloaded-module, die deze bijwerkt.
9, Autoriteit 2%
Dit is de moderne manier om een module opnieuw te laden:
from importlib import reload
Als u de versies van Python ouder dan 3,5 wilt ondersteunen, probeer dan dit:
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+
Om het te gebruiken, voert u reload(MODULE)
, vervanging van MODULE
met de module die u opnieuw wilt laden.
Bijvoorbeeld, reload(math)
wordt de math
module opnieuw laden.
10
Voor degenen zoals ik die alle modules willen verwijderen (wanneer ze worden uitgevoerd in de Python-interpreter onder Emacs):
for mod in sys.modules.values():
reload(mod)
Meer informatie vindt u in Python-modules opnieuw laden.
Antwoord 11
Enthought Traits heeft een module die hier redelijk goed voor werkt. https://traits.readthedocs.org/en/ 4.3.0/_modules/traits/util/refresh.html
Het zal elke module die is gewijzigd opnieuw laden en andere modules en instanced-objecten bijwerken die deze gebruiken. Het werkt meestal niet met __very_private__
-methoden, en kan stikken in klasse-overerving, maar het bespaart me waanzinnig veel tijd om de hosttoepassing opnieuw te moeten starten bij het schrijven van PyQt-guis, of dingen die worden uitgevoerd binnen programma’s zoals Maya of Nuke. Het werkt misschien niet 20-30% van de tijd, maar het is nog steeds ongelooflijk nuttig.
Het pakket van Enthought laadt bestanden niet opnieuw op het moment dat ze veranderen – je moet het expliciet noemen – maar dat zou niet zo moeilijk te implementeren moeten zijn als je het echt nodig hebt
Antwoord 12
Degenen die python 3 gebruiken en herladen vanuit importlib.
Als je problemen hebt alsof het lijkt alsof de module niet herlaadt… Dat komt omdat het wat tijd nodig heeft om pyc opnieuw te compileren (tot 60 sec). Ik schrijf deze hint alleen maar om te weten of je dit soort hebt meegemaakt van het probleem.
Antwoord 13
01-02-2018
- module
foo
moet vooraf succesvol zijn geïmporteerd. from importlib import reload
,reload(foo)
31.5. importlib — De implementatie van import — Python 3.6.4 documentatie
Antwoord 14
Andere optie. Zie dat Python-standaard importlib.reload
de bibliotheek die als argument is doorgegeven, gewoon opnieuw importeert. Het zal nietde bibliotheken herladen die uw lib importeert. Als je veel bestanden hebt gewijzigd en een enigszins complex pakket hebt om te importeren, moet je een diepe herlaadbeurtdoen.
Als je IPythonof Jupyteris geïnstalleerd, kunt u een functie gebruiken om alle bibliotheken diep te herladen:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Als je Jupyter niet hebt, installeer het dan met dit commando in je shell:
pip3 install jupyter
Antwoord 15
Bewerken (Antwoord V2)
De oplossing van vroeger is goed om alleen de reset-informatie te krijgen, maar het zal niet alle verwijzingen veranderen (meer dan reload
maar minder dan vereist). Om alle referenties ook daadwerkelijk in te stellen, moest ik naar de garbage collector gaan en de referenties daar herschrijven. Nu werkt het als een tierelier!
Houd er rekening mee dat dit nietwerkt als de GC is uitgeschakeld of als gegevens worden herladen die niet door de GC worden gecontroleerd. Als je niet wilt rotzooien met de GC, is het oorspronkelijke antwoord misschien genoeg voor jou.
Nieuwe code:
import importlib
import inspect
import gc
from enum import EnumMeta
from weakref import ref
_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.
"""
# 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)
Origineel antwoord
Zoals geschreven in het antwoord van @ BOBINE, als er al een verwijzing naar die module in een andere module is (vooral als deze is geïmporteerd met de as
trefwoord als import numpy as np
), Die exemplaar wordt niet overschreven.
Dit bleek me behoorlijk problematisch bij het toepassen van tests die een “clean-leate” -status van de configuratiemodules vereisten, dus ik heb een functie geschreven met de naam reset_module
die importlib
‘s reload
Functie opnieuw op en overschrijft recursief alle kenmerken van de aangegeven module. Het is getest met Python-versie 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
Opmerking: Gebruik met zorg! Gebruik deze op niet-perifere modules (modules die bijvoorbeeld extern gebruikte klassen definiëren, kunnen leiden tot interne problemen in Python (zoals Pickling / Un-Pickling-problemen).
16
Voor mij voor het geval van Abaqus is het de manier waarop het werkt.
Stel je voor dat je bestand class_verticsedges.py
is
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'])
17
Als u de volgende fout tegenkomt, kan dit antwoord u helpen om een oplossing te krijgen:
TraceBack (meest recente oproep Laatste): Bestand "ffff", lijn 1, in NameError: naam 'YJYY' is niet gedefinieerd
of
TraceBack (meest recente oproep Laatste): Bestand "ffff", lijn 1, in Bestand "/USR/LOCAL/LIB/PYTHON3.7/IMPORTLIB/__INIT__.PY", LINE 140, IN RELOAD Verhoog TypeError ("Reload () Argument moet een module zijn") TypeError: Reload () Argument moet een module zijn
Als u een importeert, zoals de onderstaande, moet u mogelijk de sys.modules
gebruiken om de module te krijgen die u opnieuw wilt laden:
import importlib
import sys
from YYYY.XXX.ZZZ import CCCC
import AAA.BBB.CC
def reload(full_name)
if full_name in sys.modules:
importlib.reload(sys.modules[full_name])
reload('YYYY.XXX.ZZZ') # this is fine in both cases
reload('AAA.BBB.CC')
importlib.reload(YYYY.XXX.ZZZ) # in my case: this fails
importlib.reload(AAA.BBB.CC) # and this is ok
Het belangrijkste probleem is dat de importlib.reload
alleen module accepteert en geen string.
Antwoord 18
Voor het verwijderen van modules uit sys.modules moeten ook ‘Geen’typen worden verwijderd.
Methode 1:
import sys
import json ## your module
for mod in [ m for m in sys.modules if m.lstrip('_').startswith('json') or sys.modules[m] == None ]: del sys.modules[mod]
print( json.dumps( [1] ) ) ## test if functionality has been removed
Methode 2, waarbij gebruik wordt gemaakt van boekingen, om alleafhankelijkheden te verwijderen:
import sys
before_import = [mod for mod in sys.modules]
import json ## your module
after_import = [mod for mod in sys.modules if mod not in before_import]
for mod in [m for m in sys.modules if m in after_import or sys.modules[m] == None]: del sys.modules[mod]
print( json.dumps( [2] ) ) ## test if functionality has been removed
Optioneel, om er zeker van te zijn dat alle inzendingen eruit zijn, als je dat wilt:
import gc
gc.collect()
Antwoord 19
Een andere manier zou kunnen zijn om de module in een functie te importeren. Op deze manier wordt de module, wanneer de functie is voltooid, verzameld.
Antwoord 20
Ik had veel problemen met het herladen van iets in Sublime Text, maar uiteindelijk kon ik dit hulpprogramma schrijven om modules op Sublime Text te herladen op basis van de code die sublime_plugin.py
gebruikt om modules te herladen.
Hieronder accepteert u dat u modules herlaadt vanaf paden met spaties op hun naam, en later na het herladen kunt u gewoon importeren zoals u gewoonlijk doet.
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()
Als je het voor de eerste keer uitvoert, zou dit de module moeten laden, maar als je later de methode/functie run_tests()
opnieuw kunt gebruiken, zal het de testbestanden opnieuw laden. Met Sublime Text (Python 3.3.6
) gebeurt dit vaak omdat de interpreter ervan nooit sluit (tenzij je Sublime Text opnieuw start, dwz de Python3.3
-interpreter).