2012-06-10 7 views
7

Estoy trabajando con un directorio temporal y quiero asegurarme de que se elimine al cerrar el programa (independientemente de si el programa fue exitoso). Estoy usando tempfile.mkdtemp para crear el directorio y poner la cadena que se crea en una subclase de str que elimina el directorio en su __del__ comando:¿Cómo se gestiona un directorio temporal de modo que se garantice su eliminación al cerrar el programa?

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()) 

Esto es lo que no estoy seguro acerca de: si el programa se cierra o se si ocurriera un KeyboardInterrupt, ¿Python automáticamente eliminará/recolectará todas las variables? Si no, ¿cómo puedo asegurarme de que el directorio se elimine?

Información relacionada sobre creating destructor methods in Python. Parece que mientras el objeto TempDir no haga referencia a nada más, usar __del__ para destruirlo debería estar bien.

+1

¿Qué has encontrado cuando lo has probado? ¿'__del__' se ejecuta cada vez que Ctrl + C? –

+0

bien, acabo de agregar los resultados de mis pruebas, ¡muchas gracias por la sugerencia de @JoelCornett! –

+0

Considere usar el módulo 'atexit' además de' __del__'. – martineau

Respuesta

17

No utilizaría un método __del__, la semántica no es confiable y podría interferir con la recolección de basura. Utilice un administrador de contexto: defina un método __enter__ y __exit__, y ponga su uso del objeto en una declaración with. Está claro, es explícito, y funcionará sin preocupaciones.

O, otra manera de hacer un gestor de contexto:

@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

pregunta sobre esto: quiero usar este directorio para crear una cantidad de archivos y luego solo eliminarlo: (1) cuando el programa finaliza o (2) cuando todas las referencias a los archivos en el directorio se han ido (lo que significa que, en mi implementación, todas las referencias a la clase TempDir también se habrían ido.) ¿Este formato podría hacer eso? –

+1

Supongo que hay un punto adicional aquí: realmente no funciona solo usar esto en una declaración con - las personas que usan el paquete van a manipular, agregar/eliminar archivos y los archivos temporales deberían ser transparentes para ellos. No parece que sea una buena opción para el administrador de contexto. –

+0

Jeff, tienes razón, el administrador de contexto es bueno cuando la vida de un recurso coincide con un alcance léxico. El administrador de contexto aún se puede usar en el nivel superior de su programa para manejar la salida del programa, mientras que otras referencias a objetos se pueden administrar explícitamente para tratar con archivos que hacen referencia al directorio. –

1

Elimina todo solo cuando el programa finaliza (como normalmente lo haría).

Para ilustrar, aquí está el código que utilicé:

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()) 

y de salida:

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

Si lo ejecuta en modo interactivo, que no elimina hasta que salga del programa.

$ 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 

Por último, si se agrega una línea raw_input('') al archivo, se comportará exactamente igual a fin de programa si pulsa Ctrl-C.

7

necesitaba algo similar para un conjunto de pruebas paquete que dependía de la existencia de un específico (semi-burlado) estructura de archivos. Con muchos módulos de prueba, no siempre sé qué pruebas se ejecutarán y en qué orden, o cómo terminará la ejecución de la prueba.

El uso de __del__ en la salida de la aplicación no es confiable en mi experiencia. Y usar el administrador de contexto significaría reescribir los archivos de prueba para asegurarse de que todo se ajuste bien. En cambio, uso atexit. En <package>.tests.__init__ Acabo de añadir:

import atexit, shutil, tempfile 

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

Python llamará entonces shutil.rmtree(test_area) en la salida. Los argumentos de palabras clave para el manejo de errores también pueden agregarse si es necesario.

Cuestiones relacionadas