2010-05-27 9 views
5

In einigen Python-Unit-Tests eines Programms, an dem ich gerade arbeite, verwenden wir In-Memory-Zip-Dateien für End-to-End-Tests. In SetUp() erstellen wir eine einfache Zip-Datei, aber in einigen Tests wollen wir einige Archive überschreiben. Dazu machen wir "zip.writestr (Archivname, zip.read (Archivname) + neuer_content)". So etwas wiePython 2.6 mag es nicht, an vorhandene Archive in Zip-Dateien anzuhängen

import zipfile 
from StringIO import StringIO 

def Foo(): 
    zfile = StringIO() 
    zip = zipfile.ZipFile(zfile, 'a') 
    zip.writestr(
     "foo", 
     "foo content") 
    zip.writestr(
     "bar", 
     "bar content") 
    zip.writestr(
     "foo", 
     zip.read("foo") + 
     "some more foo content") 
    print zip.read("bar") 

Foo() 

Das Problem ist, dass das funktioniert gut in Python 2.4 und 2.5, aber nicht 2.6. In Python 2.6 scheitert dies an der Druckzeile mit "BadZipfile: Dateiname in Verzeichnis" bar "und Header" foo "unterscheiden sich."

Es scheint, dass es die richtige Dateizeile liest, aber dass es denkt, dass es foo stattdessen lesen sollte.

Ich bin ratlos. Was mache ich falsch? Wird das nicht unterstützt? Ich habe versucht, das Internet zu durchsuchen, konnte aber keine ähnlichen Probleme finden. Ich habe die Zipfile-Dokumentation gelesen, konnte aber nichts finden (was ich für relevant hielt), besonders da ich read() mit der Dateinamen-Zeichenfolge aufruft.

Irgendwelche Ideen?

Vielen Dank im Voraus!

Antwort

2

Die PKZIP-Datei ist sehr strukturiert und nur das Anhängen an das Ende wird das vermasseln. Ich kann nicht mit früheren Versionen arbeiten, aber die Problemumgehung für dieses Problem besteht darin, eine ZIP-Datei zum Lesen zu öffnen, eine neue zum Schreiben zu öffnen, den Inhalt der ersten zu extrahieren und dann am Ende die zusätzlichen Komponenten hinzuzufügen. Wenn Sie fertig sind, ersetzen Sie die ursprüngliche ZIP-Datei durch die neu erstellte ZIP-Datei.

Die Zurückverfolgungs ich erhalte, wenn der Code ausgeführt wird, wenn der Code ausgeführt wird:

Traceback (most recent call last): 
    File "zip.py", line 19, in <module> 
    Foo() 
    File "zip.py", line 17, in Foo 
    print zip.read("bar") 
    File "/usr/lib/python2.6/zipfile.py", line 834, in read 
    return self.open(name, "r", pwd).read() 
    File "/usr/lib/python2.6/zipfile.py", line 874, in open 
    zinfo.orig_filename, fname) 
zipfile.BadZipfile: File name in directory "bar" and header "foo" differ. 

Bei genauerem Hinsehen merke ich, dass Sie aus einer Datei artigen StringIO geöffnet mit ‚a'ppend Modus lesen die sollte Dies führt zu einem Lesefehler, da 'a' im Allgemeinen nicht lesbar ist und sicherlich zwischen Lese- und Schreibvorgängen gesucht() werden muss. Ich mache ein paar herum und aktualisiere das.

Update:

Nachdem so ziemlich alle dieser Code von Doug Hellmann gestohlen ausgezeichnete Python Module of the Week finde ich, dass es ziemlich viel arbeitet wie ich erwartet hatte. Man kann nicht nur auf eine strukturierte PKZIP Datei anhängen, und wenn der Code in der ursprünglichen Nachricht jemals funktioniert hat, war es durch Zufall:

import zipfile 
import datetime 

def create(archive_name): 
    print 'creating archive' 
    zf = zipfile.ZipFile(archive_name, mode='w') 
    try: 
     zf.write('/etc/services', arcname='services') 
    finally: 
     zf.close() 

def print_info(archive_name): 
    zf = zipfile.ZipFile(archive_name) 
    for info in zf.infolist(): 
     print info.filename 
     print '\tComment:\t', info.comment 
     print '\tModified:\t', datetime.datetime(*info.date_time) 
     print '\tSystem:\t\t', info.create_system, '(0 = Windows, 3 = Unix)' 
     print '\tZIP version:\t', info.create_version 
     print '\tCompressed:\t', info.compress_size, 'bytes' 
     print '\tUncompressed:\t', info.file_size, 'bytes' 
     print 
    zf.close() 

def append(archive_name): 
    print 'appending archive' 
    zf = zipfile.ZipFile(archive_name, mode='a') 
    try: 
     zf.write('/etc/hosts', arcname='hosts') 
    finally: 
     zf.close() 

def expand_hosts(archive_name): 
    print 'expanding hosts' 
    zf = zipfile.ZipFile(archive_name, mode='r') 
    try: 
     host_contents = zf.read('hosts') 
    finally: 
     zf.close 

    zf = zipfile.ZipFile(archive_name, mode='a') 
    try: 
     zf.writestr('hosts', host_contents + '\n# hi mom!') 
    finally: 
     zf.close() 

def main(): 
    archive = 'zipfile.zip' 
    create(archive) 
    print_info(archive) 
    append(archive) 
    print_info(archive) 
    expand_hosts(archive) 
    print_info(archive) 

if __name__ == '__main__': main() 

Bemerkenswert ist die Ausgabe von dem letzten Aufruf print_info:

... 
hosts 
    Modified: 2010-05-20 03:40:24 
    Compressed: 404 bytes 
    Uncompressed: 404 bytes 

hosts 
    Modified: 2010-05-27 11:46:28 
    Compressed: 414 bytes 
    Uncompressed: 414 bytes 

Es wurde nicht an den vorhandenen ArcName 'Hosts' angefügt, es wurde ein zusätzliches Archivmitglied erstellt.

"Je n'ai fait celle-ci und longue que parce que je n'ai pas eu le loisir de la faire und courte."
- Blaise Pascal

0

Die ZIP-Datei-Format wurde entwickelt, um angehängt zu werden.Es kann weitere Dateien mit demselben Namen hinzufügen und das letzte extrahieren, aber ZipFile ist nicht zum gleichzeitigen Lesen und Schreiben ausgelegt. Sie müssen die Datei schließen, um die Endsätze (https://hg.python.org/cpython/file/2.7/Lib/zipfile.py#l1263) auszugeben, die dann erneut mit der Methode open() oder read() eingelesen werden. (https://hg.python.org/cpython/file/2.7/Lib/zipfile.py#l933)

import zipfile 
from StringIO import StringIO 

def Foo(): 
    zfile = StringIO() 

    zip = zipfile.ZipFile(zfile, 'a') 
    zip.writestr(
     "foo", 
     "foo content") 
    zip.writestr(
     "bar", 
     "bar content") 
    zip.close() 

    zip = zipfile.ZipFile(zfile, 'r') 
    foo_content = zip.read("foo") 

    zip2 = zipfile.ZipFile(zfile, 'a') 
    zip2.writestr(
     "foo", 
     foo_content + 
     "some more foo content") 
    print zip2.namelist() 
    print zip2.read("bar") 

Foo() 

Output:

pyzip.py:23: UserWarning: Duplicate name: 'foo' 
    "some more foo content") 
['foo', 'bar', 'foo'] 
bar content