2013-04-28 8 views
5

Voglio prendere un numero arbitrario di percorsi che rappresentano archivi tar annidati ed eseguire un'operazione sull'archivio più interno. Il problema è che il nesting può essere arbitrario, quindi anche il numero di gestori di contesto di cui ho bisogno è arbitrario.Come posso annidare un numero arbitrario di gestori di contesto di file Python?

Prendiamo, per esempio:

ARCHIVE_PATH = "path/to/archive.tar" 

INNER_PATHS = (
    "nested/within/archive/one.tar", 
    "nested/within/archive/two.tar", 
    # Arbitary number of these 
) 

def list_inner_contents(archive_path, inner_paths): 
    with TarFile(archive_path) as tf1: 
     with TarFile(fileobj=tf1.extractfile(inner_paths[0])) as tf2: 
      with TarFile(fileobj=tf2.extractfile(inner_paths[1])) as tf3: 
       # ...arbitary level of these! 
       return tfX.getnames() 

contents = list_inner_contents(ARCHIVE_PATH, INNER_PATHS)) 

Non posso utilizzare il with del nesting syntax perché ci potrebbe essere un qualsiasi numero di livelli a nido dichiarazione. Non posso usare contextlib.nested perché i documenti dicono che proprio lì:

... usando nested() per aprire due file è un errore di programmazione come il primo file non verrà chiuso prontamente se viene generata un'eccezione quando si apre il secondo file.

C'è un modo per usare costrutti di linguaggio per fare questo, o devo gestire manualmente la mia pila di oggetti di file aperti?

+2

In 3.3, è possibile utilizzare [ 'contextlib.ExitStack'] (http: //docs.python. org/3/library/contextlib.html # contextlib.ExitStack). – delnan

+0

@delnan - Ho una ** dipendenza ** che mi impedisce di usare Python 3:/ – detly

+0

@delnan Nice! Non sapevo che questo fosse stato aggiunto in python3.3. Sembra una soluzione davvero pulita. – Bakuriu

risposta

4

In questo caso è possibile utilizzare la ricorsione. Ci si sente ad essere più naturale per il caso (naturalmente se non c'è un trattamento speciale in Python ancora):

ARCHIVE_PATH = "path/to/archive.tar" 

INNER_PATHS = [ 
    "nested/within/archive/one.tar", 
    "nested/within/archive/two.tar", 
    # Arbitary number of these 
] 

def list_inner_contents(archive_path, inner_paths): 
    def rec(tf, rest_paths): 
     if not rest_paths: 
      return tf.getnames() 

     with TarFile(fileobj=tf.extractfile(rest_paths[0])) as tf2: 
      return rec(tf2, rest_paths[1:]) 

    with TarFile(archive_path) as tf: 
     try: 
      return rec(tf, inner_paths) 
     except RuntimeError: 
      # We come here in case the inner_paths list is too long 
      # and we go too deeply in the recursion 
      return None 
+1

Questa sembra l'unica soluzione semplice. Si potrebbe scrivere un gestore di contesto personalizzato che chiama manualmente i metodi '__enter__' e' __exit__', ma poi diventa davvero difficile gestire le eccezioni per farlo comportarsi come previsto. – Bakuriu

Problemi correlati