2012-06-10 10 views
7

Ich arbeite mit einem temporären Verzeichnis und ich möchte sicherstellen, dass es beim Schließen des Programms gelöscht wird (unabhängig davon, ob das Programm erfolgreich war). Ich verwende tempfile.mkdtemp das Verzeichnis zu erstellen und setzen Sie die Zeichenfolge, die in eine Unterklasse von str erstellt wird, der das Verzeichnis auf seinem __del__ Befehl löscht:Wie verwalten Sie ein temporäres Verzeichnis, so dass es beim Schließen des Programms garantiert gelöscht wird?

import shutil 
import tempfile 

class TempDir(str): 
    """ container for temporary directory. 
    Deletes directory when garbage collected/zero references """ 
    def __del__(self): 
     shutil.rmtree(self.__str__(), onerror=my_error_fn) 

dbdir = TempDir(tempfile.mkdtemp()) 

Hier ist, was ich bin mir nicht sicher: wenn das Programm geschlossen oder ein KeyboardInterrupt passiert, wird Python automatisch alle Variablen löschen/abräumen? Wenn nicht, wie kann ich sicherstellen, dass das Verzeichnis gelöscht wird?

Verwandte Informationen über creating destructor methods in Python. Scheint so lange, wie das TempDir-Objekt nichts anderes referenziert, mit __del__ zu zerstören, sollte es in Ordnung sein.

+1

Was haben Sie gefunden, als Sie es getestet haben? Wird '__del__' jedes Mal ausgeführt, wenn Sie Strg + C drücken? –

+0

Okay, ich habe nur die Ergebnisse meiner Tests hinzugefügt - vielen Dank für den Vorschlag @JoelCornett! –

+0

Verwenden Sie das 'atexit'-Modul zusätzlich zu' __del__'. – martineau

Antwort

17

Ich würde keine __del__ Methode verwenden, die Semantik ist unzuverlässig und könnte die Garbage Collection beeinträchtigen. Verwenden Sie einen Kontextmanager: Definieren Sie eine __enter__ und __exit__ Methode, und verwenden Sie das Objekt in einer with Anweisung. Es ist klar, es ist explizit, und es wird ohne Sorge funktionieren.

Oder ein anderer Weg, um einen Kontext-Manager zu machen:

@contextlib.contextmanager 
def tempdir(prefix='tmp'): 
    """A context manager for creating and then deleting a temporary directory.""" 
    tmpdir = tempfile.mkdtemp(prefix=prefix) 
    try: 
     yield tmpdir 
    finally: 
     shutil.rmtree(tmpdir) 
+1

Frage dazu: Ich möchte dieses Verzeichnis verwenden, um eine Reihe von Dateien zu erstellen und sie dann nur entweder zu löschen: (1) wenn das Programm endet oder (2) wenn alle Verweise auf Dateien im Verzeichnis weg sind (was bedeutet, dass Meine Implementierung, alle Verweise auf die TempDir-Klasse wären auch weg.) Wäre dieses Format in der Lage das zu tun? –

+1

Ich denke, ein weiterer Punkt hier: es funktioniert nicht wirklich, nur dies in einer With-Anweisung zu verwenden - Menschen mit dem Paket werden manipulieren, Hinzufügen/Entfernen von Dateien durch und die temporären Dateien sollten für sie transparent sein. Es scheint nicht so, als ob das für den Kontextmanager gut geeignet wäre. –

+0

Jeff, Sie haben Recht, der Kontextmanager ist gut, wenn die Lebensdauer einer Ressource einem lexikalischen Geltungsbereich entspricht. Der Kontextmanager kann weiterhin auf der obersten Ebene Ihres Programms verwendet werden, um mit dem Programm-Exit umzugehen, während andere Objektreferenzen explizit verwaltet werden können, um mit Dateien zu arbeiten, die auf das Verzeichnis verweisen. –

1

Es löscht alles nur, wenn das Programm endet (wie es normalerweise wäre).

veranschaulicht, hier ist der Code, den ich verwenden:

import tempfile 
import shutil 
import os 

class TempDir(str): 
    """ container for temporary directory. Deletes directory when garbage 
    collected/zero references """ 
    def __del__(self): 
     print "deleting temporary files in directory {}".format(self.__str__()) 
     shutil.rmtree(self.__str__(), onerror=delete_dir_handler) 

def delete_dir_handler(listdir_fn, dirname, exception): 
    try: 
     for fileobj in listdir_fn(dirname): 
      os.remove(fileobj) 
     os.removedirs(dirname) 
    except (AttributeError, OSError, TypeError): 
     print exception[1] 
     print "Sorry. Couldn't delete directory {}.".format(dirname) 
     raise 

test = TempDir(tempfile.mkdtemp()) 

und Ausgang:

$ python my_file.py 
deleting temporary files in directory /var/folders/A9/A9xrfjrXGsq9Cf0eD2jf0U+++TI/-Tmp-/tmpG3h1qD 

Wenn Sie es im interaktiven Modus ausführen, wird es nicht löschen, bis Sie das Programm beenden.

$ python -i my program 
>>> # pressed Ctrl-C here 
KeyboardInterrupt 
>>> # again, Ctrl-C 
KeyboardInterrupt 
>>> # Ctrl-D to exit 
deleting temporary files in directory /var/folders/A9/A9xrfjrXGsq9Cf0eD2jf0U+++TI/-Tmp-/tmpMBGt5n 

Schließlich, wenn Sie eine raw_input('') Zeile in die Datei hinzufügen, wird es verhalten sich genau das gleiche wie das Programmende, wenn Sie bei gedrückter Strg-C drücken.

7

ich etwas ähnliches für ein Paket-Testsuite benötigt, die auf die Existenz eines spezifischen abhing (halb verspottet) Dateistruktur. Bei vielen Testmodulen weiß ich nicht immer, welche Tests in welcher Reihenfolge ausgeführt werden oder wie der Testlauf abläuft.

Verwendung __del__ bei Application Exit ist meiner Erfahrung nach unzuverlässig. Und die Verwendung von Context Manager würde bedeuten, die Testdateien neu zu schreiben, um sicherzustellen, dass alles gut verpackt ist. Stattdessen verwende ich atexit. In <package>.tests.__init__ Habe ich nur noch:

import atexit, shutil, tempfile 

test_area = tempfile.mkdtemp() 
atexit.register(shutil.rmtree, test_area) 

Python wird dann shutil.rmtree(test_area) an der Ausfahrt nennen. Schlüsselwortargumente für die Fehlerbehandlung können bei Bedarf ebenfalls hinzugefügt werden.

Verwandte Themen