2013-03-19 5 views
16

Wenn ich Python-Code bin entwickeln, ich teste es in der Regel in einer Ad-hoc-Weise im Interpreter. Ich werde import some_module, testen, einen Fehler finden, beheben den Fehler und speichern, und verwenden Sie dann die eingebaute in reload Funktion reload(some_module) und erneut testen.rekursive Version von 'Reload'

jedoch annehmen, dass in some_module Ich habe import some_other_module, und während some_module Testen entdecke ich einen Fehler in some_other_module und beheben. Jetzt ruft reload(some_module) nicht rekursiv some_other_module erneut importieren. Ich muss die Abhängigkeit entweder manuell neu importieren (indem ich etwas wie reload(some_module.some_other_module) oder import some_other_module; reload(some_other_module) mache, oder, wenn ich eine ganze Reihe von Abhängigkeiten geändert habe und nicht mehr nachgespielt habe, was ich neu laden muss, muss ich den gesamten Interpreter neu starten.

Was hat sein bequemer ist, wenn es einige recursive_reload Funktion waren und ich konnte nur tun recursive_reload(some_module) und haben Python nicht nur some_module neu zu laden, sondern auch neu zu laden rekursiv jedes Moduls, some_module Importe (und jedes Modul, dass jedes dieser Module Importe, und so weiter), so dass ich konnte sicher sein, dass ich nicht eine alte Version von einem der anderen Module verwendet, auf denen some_module abhängt.

Ich glaube nicht, dass Python etwas eingebaut hat, das sich wie die recursive_reload-Funktion verhält, die ich hier beschreibe, aber gibt es eine einfache Möglichkeit, so etwas zusammen zu hacken?

+0

möglich Duplikat [wie Liste der Module zu finden, die in Python auf einen bestimmten Modul abhängen] (http://stackoverflow.com/questions/1827629/how-to-find-list-of-modules- which-depend-on-a-specific-Modul-in-Python) – jsbueno

Antwort

21

Ich habe gegen das gleiche Problem läuft, und du mich inspiriert, um tatsächlich das Problem zu lösen.

from types import ModuleType 

def rreload(module): 
    """Recursively reload modules.""" 
    reload(module) 
    for attribute_name in dir(module): 
     attribute = getattr(module, attribute_name) 
     if type(attribute) is ModuleType: 
      rreload(attribute) 

Oder, wenn Sie IPython arbeiten, verwenden Sie nur dreload oder --deep-reload beim Start übergeben.

+2

Akzeptieren Sie dies, obwohl es zu beachten ist, dass es einen Fall gibt, den es nicht behandelt: wenn 'moduleA' und' moduleB', die in keiner Weise voneinander abhängig sind, beide importiert worden sind, und dann 'moduleA' wird modifiziert, um' modulB' zu importieren, dann ruft 'rreload (moduleA)' '' moduleB' nicht so auf, wie es sollte. Dies ist ein geringfügiger Fehler, um mich in den Fällen nicht zu interessieren, für die ich dies verwenden würde, obwohl. –

+1

Etwas sagt mir, dass Sie dieses Problem lösen können, indem Sie 'reload (modul)' an den Anfang der Funktion verschieben. –

+0

Eigentlich, jetzt, wo ich damit experimentiere, wollen wir vielleicht 'reload (modul)' zweimal aufrufen; vor und nach dem Nachladen von Submodulen. Wir müssen es vorher anrufen, falls es irgendwelche neuen Module betrifft. Wir müssen es danach aufrufen, um Anweisungen wie 'von X-Import Y' zu behandeln. Wenn 'X' gerade neu geladen wurde, müssen wir' Y' nocheinmal importieren. Ich vermute, dass es viel schwieriger sein kann, also müssten wir so lange nachladen, bis sich der Staub gelegt hat. – osa

3

Wäre es nicht einfacher sein, tatsächlich einige Testfälle zu schreiben und jedes Mal, wenn Sie fertig sind mit modifizierenden Ihr Modul laufen?

Was Sie tun, ist cool (Sie sind im Wesentlichen TDD (Test Driven Development mit), aber sie tun es falsch.

Bedenken Sie, dass mit schriftlicher Unit-Tests (der Standard-Python unittest Modul verwenden, oder besser noch nose) Sie haben Tests erhalten, die wiederverwendbar, stabil und helfen Sie sind inconsitencies in Ihrem Code viel viel schneller und besser als mit Prüfung des Moduls in der interaktiven Umgebung erkennen.

+0

+1, Es klingt wie diese Testmethodik wurde übergewachsen. Es muss nicht einmal ein echter Fall sein, sondern nur ein kleines Skript, das als Zwischenspeicher für den Test von Code dient (wenn das Projekt klein genug ist, dass vollständige Tests übertrieben sind). –

+2

Ironie: Ich wurde provoziert, diese Frage zu stellen, nachdem ich auf dieses Problem * beim Testen einiger Komponententests, die ich für eine andere Anwendung geschrieben habe, * hingewiesen habe. Wenn ich Ihren Rat hier anwenden würde, würde ich endlos viele Wiederholungen von Tests durchlaufen. ;) –

+2

Im Ernst: Richtige Tests haben natürlich ihren Platz, aber manchmal ist ein etwas lässigerer Ansatz gefragt (auch wenn es zusätzlich zu den eigentlichen Tests ist), und ein Teil dessen, was Python ausmacht, ist, dass es das macht dieser lockere Ansatz ist einfach. Vielleicht habe ich eine halbe Funktion geschrieben und möchte überprüfen, ob es bisher das ausgibt, was ich erwarte, oder vielleicht habe ich eine ganze Reihe von Druckanweisungen in einen kniffligen Code geschrieben und möchte die Funktion auf einige perverse Parameter anwenden Ich spezifiziere und sehe, was gedruckt wird. Komponententests gelten in diesen Szenarien nicht. –

0

Es ist eine heikle Sache zu tun - ich habe ein funktionierendes Beispiel in dieser Antwort: how to find list of modules which depend upon a specific module in python

+0

Ich habe den Code nicht gelesen und versucht, ihn zu verstehen, aber ich habe versucht, die defs in den Interpreter einzufügen und reload_dependences auf meinem Modul aufzurufen: 'Traceback (letzter Aufruf zuletzt): Datei" ", Zeile 1, in Datei "", Zeile 9, in reload_dependences ImportError: Internes Modul kann nicht neu initialisiert werden __main__' –

+0

Nun - ich denke, die Fehlermeldung ist, dass es versucht hat, das "__main__" -Modul selbst zu übertragen - in diesem Fall Ihr Interpreter Session.Versuchen Sie, diesen Code in eine separate Datei einzufügen, die Sie in den IIRC des itnerpreter importieren, so wie ich ihn entwickelt habe. – jsbueno

1

Technisch in jeder Sie Datei, einen Reload-Befehl setzen könnte, um sicherzustellen, dass sie jedes Mal neu geladen es importiert

a.py:

def testa(): 
    print 'hi!' 

b.py:

import a 
reload(a) 
def testb(): 
    a.testa() 

nun interaktiv:

import b 
b.testb() 
#hi! 

#<modify a.py> 

reload(b) 
b.testb() 
#hello again! 
+1

Sicher könnte ich das tun, aber dann wären meine eigentlichen Code-Dateien hässlich und ineffizient. Es macht mir nichts aus, alberne Hacks zu benutzen, wenn ich im interaktiven Interpreter spiele, aber ich würde es vorziehen, keinen dummen Hack in jede Datei in meinem Code einzubauen, nur um mich ein wenig fauler zu machen, wenn ich im Interpreter bin . :) –

1

Ich habe gegen das gleiche Problem gelaufen und ich habe auf @Mattew und @osa Antwort aufgebaut.

from types import ModuleType 
import os, sys 
def rreload(module, paths=None, mdict=None): 
    """Recursively reload modules.""" 
    if paths is None: 
     paths = [''] 
    if mdict is None: 
     mdict = {} 
    if module not in mdict: 
     # modules reloaded from this module 
     mdict[module] = [] 
    reload(module) 
    for attribute_name in dir(module): 
     attribute = getattr(module, attribute_name) 
     if type(attribute) is ModuleType: 
      if attribute not in mdict[module]: 
       if attribute.__name__ not in sys.builtin_module_names: 
        if os.path.dirname(attribute.__file__) in paths: 
         mdict[module].append(attribute) 
         rreload(attribute, paths, mdict) 
    reload(module) 
    #return mdict 

Es gibt drei Unterschiede:

  1. Im allgemeinen Fall, reload (Modul) hat am Ende der Funktion als auch genannt werden, wie @osa hingewiesen.
  2. Mit zirkulären Importabhängigkeiten würde der Code, der früher veröffentlicht wurde, für immer loopen, also habe ich ein Wörterbuch von Listen hinzugefügt, um den Satz von Modulen zu verfolgen, die von anderen Modulen geladen werden. Während zirkuläre Abhängigkeiten nicht cool sind, erlaubt Python sie, so dass diese Reload-Funktion auch mit ihnen umgeht.
  3. Ich habe eine Liste von Pfaden hinzugefügt (Standard ist ['']), von denen das Neuladen erlaubt ist. Einige Module wurden nicht wie üblich neu geladen (wie gezeigt here).
+0

Sie haben einen Diktat-Standardparameter, den Sie in der Funktion ändern. Wenn Sie die Funktion ein zweites Mal aufrufen, wird das gleiche Diktat verwendet und nichts neu geladen. –

+0

Das Mdict-Wörterbuch wird jedes Mal neu gefüllt, wenn ich die Rreload-Funktion aufruft. Ich verwende diese Funktion momentan und rufe sie regelmäßig an, wenn ich den Code ändere (also auch mehrfach, ohne den Interpreter neu zu laden) und es funktioniert ... – redsk

1

Ich fand die Antwort von redsk sehr nützlich. Ich schlage eine vereinfachte (für den Benutzer, nicht als Code) Version vor, wo der Pfad zum Modul automatisch gesammelt wird und die Rekursion für eine beliebige Anzahl von Ebenen funktioniert. Alles ist in sich in einer einzigen Funktion enthalten. Getestet auf Python 3.4. Ich denke, für Python 3.3 muss man reload() von imp anstelle von importlib importieren. Es überprüft auch, ob die Datei Datei vorhanden ist, die falsch sein kann, wenn der Coder vergisst, eine init.py Datei im Submodul zu definieren. In diesem Fall wird eine Ausnahme ausgelöst.

def rreload(module): 
    """ 
    Recursive reload of the specified module and (recursively) the used ones. 
    Mandatory! Every submodule must have an __init__.py file 
    Usage: 
     import mymodule 
     rreload(mymodule) 

    :param module: the module to load (the module itself, not a string) 
    :return: nothing 
    """ 

    import os.path 
    import sys 

    def rreload_deep_scan(module, rootpath, mdict=None): 
     from types import ModuleType 
     from importlib import reload 

     if mdict is None: 
      mdict = {} 

     if module not in mdict: 
      # modules reloaded from this module 
      mdict[module] = [] 
     # print("RReloading " + str(module)) 
     reload(module) 
     for attribute_name in dir(module): 
      attribute = getattr(module, attribute_name) 
      # print ("for attr "+attribute_name) 
      if type(attribute) is ModuleType: 
       # print ("typeok") 
       if attribute not in mdict[module]: 
        # print ("not int mdict") 
        if attribute.__name__ not in sys.builtin_module_names: 
         # print ("not a builtin") 
         # If the submodule is a python file, it will have a __file__ attribute 
         if not hasattr(attribute, '__file__'): 
          raise BaseException("Could not find attribute __file__ for module '"+str(attribute)+"'. Maybe a missing __init__.py file?") 

         attribute_path = os.path.dirname(attribute.__file__) 

         if attribute_path.startswith(rootpath): 
          # print ("in path") 
          mdict[module].append(attribute) 
          rreload_deep_scan(attribute, rootpath, mdict) 

    rreload_deep_scan(module, rootpath=os.path.dirname(module.__file__))