iMath
iMath

Reputation: 2478

The right way to use zipimport; "zipimport.ZipImportError: not a Zip file" on a seemingly valid file

I have managed to use this module without installation - just import it from path to use ,

import sys 
url = 'https://example.com' 
sys.path.insert(0, r'C:\Users\i\Downloads\you-get-0.4.1128\src')  # 
from you_get import common 
common.any_download(url, info_only=True)#NoneType 

It seems possible in Python to use zipimport to directly use the zip archive of the module without extraction, I wonder what is the right way to use zipimport, a simple trying like the following just gives the exception . I downloaded the file from here , the file C:\Users\i\Downloads\you-get-0.4.1128.zip does exist and isn't corrupted.

>>> import zipimport 
>>> zipimport.zipimporter(r'C:\Users\i\Downloads\you-get-0.4.1128.zip') 
Traceback (most recent call last): 
  File "<pyshell#1>", line 1, in <module> 
    zipimport.zipimporter(r'C:\Users\i\Downloads\you-get-0.4.1128.zip') 
zipimport.ZipImportError: not a Zip file: 'C:\\Users\\i\\Downloads\\you-get-0.4.1128.zip' 
>>> 

Upvotes: 1

Views: 3650

Answers (2)

ivan_pozdeev
ivan_pozdeev

Reputation: 35998

(This is the suggested way of action that answers your question: "The right way to use zipimport"; see further below for the immediate cause of your error.)

You shouldn't use zipimport directly. Instead, you should add the .zip file to sys.path -- it will be used as if it was a directory.

That said, the file you downloaded is a source distribution -- it has a setup.py in root and the actual modules in a subdirectory. To use the module, you need a built distribution instead.

Telling all about source and built distributions is beyond the scope of a single answer. One possible way to go is to:

  • unpack the .zip
  • make a wheel using its setup.py with python setup.py bdist_wheel and
  • install it with pip install <path to .whl>

Debugging with Visual Studio shows that this is the code that it chokes on:

v3.6.5,Modules\zipimport.c:

if (fseek(fp, -22, SEEK_END) == -1) {
    <...>
}
header_position = (unsigned long)ftell(fp);
<...>
if (fread(buffer, 1, 22, fp) != 22) {
    <...>
}
if (get_uint32(buffer) != 0x06054B50u) {
    /* Bad: End of Central Dir signature */
    errmsg = "not a Zip file";
    goto invalid_header;
}

As you can see, it reads and validates the last 22 bytes of the file as an "end of central dir signature".

The spec says:

4.3.1 A ZIP file MUST contain an "end of central directory record".

<...>

4.3.6 Overall .ZIP file format:

<...>
[end of central directory record]

4.3.16 End of central directory record:

  end of central dir signature    4 bytes  (0x06054b50)
  number of this disk             2 bytes
  number of the disk with the
  start of the central directory  2 bytes
  total number of entries in the
  central directory on this disk  2 bytes
  total number of entries in
  the central directory           2 bytes
  size of the central directory   4 bytes
  offset of start of central
  directory with respect to
  the starting disk number        4 bytes
  .ZIP file comment length        2 bytes
  .ZIP file comment       (variable size)

As you can see, this "End of central directory record" is 22 bytes.. without the comment. And this file does have a comment:

$ xxd -s 0x322b5 -g 1 you-get-0.4.1128.zip
000322b5: 50 4b 05 06 00 00 00 00 af 00 af 00 25 45 00 00  PK..........%E..
000322c5: 90 dd 02 00 28 00 61 30 62 39 37 65 35 36 65 35  ....(.a0b97e56e5
000322d5: 36 35 38 36 33 35 62 35 63 35 66 32 66 33 32 65  658635b5c5f2f32e
000322e5: 38 62 38 63 31 34 62 64 33 35 61 65 62 33        8b8c14bd35aeb3

So this is a bug. Here's a relevant ticket.

Upvotes: 1

lenik
lenik

Reputation: 23508

I have downloaded the file and have the same exception, though file seems to be legit.

Maybe you should use zipfile instead:

>>> import zipfile
>>> zipfile.ZipFile( 'you-get-0.4.1128.zip' )
<zipfile.ZipFile object at 0x7fc515343c50>
>>>

Upvotes: 0

Related Questions