Search code examples
pythoncx-freezeauto-update

cx_Freeze: LookupError: unknown encoding: cp437


I use cx_Freeze to create an exe of my software and I need to implement an auto updater. So what I do so far is I check if there is an updated version of the software and if there is the program is supposed to download a zip-file, which contains the update. So far so good. But when it comes to install the update by unpacking the zip-file I run into a LookupError:

  File "C:\Program Files (x86)\Python37-32\lib\tkinter\__init__.py", line 1705, in __call__
    return self.func(*args)
  File "Rocketupload.py", line 1947, in download
  File "C:\Program Files (x86)\Python37-32\lib\zipfile.py", line 1222, in __init__
    self._RealGetContents()
  File "C:\Program Files (x86)\Python37-32\lib\zipfile.py", line 1327, in _RealGetContents
    filename = filename.decode('cp437')
LookupError: unknown encoding: cp437

Line 1947 of Rocketupload:

z = zipfile.ZipFile(file_name, allowZip64=True)

Weirdly I do not have the same problem when I run the software by shell, without the exe created by cx_Freeze. So I reckon the problem is involved in the creation of the exe.

I will also provide the code which I use to download and unpack the updated-version-zip:

def download(self):
    exePath = sys.argv[0]
    self.downloadBtn.configure(state="disabled")
    file_name = SettingsManager.find_data_file("RocketUpload_v" + str(self.versionNumber) + ".zip")
    with open(file_name, "wb") as f:
        response = requests.get(self.downloadFile, stream=True)
        print(response)
        total_length = response.headers.get('content-length')

        dl = 0
        total_length = int(total_length)
        for data in response.iter_content(chunk_size=4096):
            downloadedSize = str(round(int(dl) / self.MBFACTOR, 2))
            if (downloadedSize[-3]) != ".":
                downloadedSize += "0"

            self.progressVar.set("RocketUpload_v" + str(self.versionNumber) + " Downloading: [" + downloadedSize + "MB/" + str(round(int(self.maximum) / self.MBFACTOR, 2)) + "MB]")
            dl += len(data)
            self.num.set(dl)
            self.parent.update()
            f.write(data)
        self.progressVar.set("RocketUpload_v" + str(self.versionNumber) + " Finished: [" + str(round(int(self.maximum) / self.MBFACTOR, 2)) + "MB/" + str(round(int(self.maximum) / self.MBFACTOR, 2)) + "MB]")
        time.sleep(1)
        self.progressVar.set("RocketUpload_v" + str(self.versionNumber) + ": Installing...")
        f.close()

    excluded = set(["rsc"])
    for root, dirs, files in os.walk(SettingsManager.find_data_file("."), topdown=True):
        dirs[:] = [d for d in dirs if d not in excluded]
        for filename in files:
            file_path = os.path.join(root,filename)
            if file_path[-4:] != ".zip":
                os.rename(file_path, file_path + ".tmp")

    time.sleep(2)

    z = zipfile.ZipFile(file_name, allowZip64=True) #<---- That is where the error happens
    uncompress_size = sum((file.file_size for file in z.infolist()))
    print(uncompress_size)
    self.progress.configure(maximum=uncompress_size)
    self.num.set(0)
    extracted_size = 0

    for file in z.infolist():
        self.progressVar.set("RocketUpload_v" + str(self.versionNumber) + " Installing: [" + str(int(extracted_size/uncompress_size*100)) + "%/100%]")
        extracted_size += file.file_size
        self.num.set(extracted_size)
        self.parent.update()
        z.extract(file)

    self.progressVar.set("RocketUpload_v" + str(self.versionNumber) + " Installing: [100%/100%]")
    self.parent.update()
    time.sleep(0.5)

    self.progressVar.set("RocketUpload_v" + str(self.versionNumber) + " Installing: Finished. Relaunch App Now.")
    self.parent.update()
    self.finished = True
    f.close()

What I'm trying to do is to rename all files regarding the running software to temporary files and then unpack the zip file, because its not allowed to overwrite opened files.

Here is my setup.py to freeze my software with cx_Freeze:

import sys
import setuptools
from cx_Freeze import setup, Executable

# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
    base = "Win32GUI"

setup(  name = "RocketUpload",
        version = "1.0",
        options = {
            "build_exe": {
                "packages": ["os","time","csv","sys","shutil","re","random","pickle","traceback", "autoit", "ctypes", "threading", "_thread", "configparser", "platform", "subprocess", "queue", "multiprocessing", "psutil", "datetime", "webbrowser","PIL","tkinter","cryptography","abc","selenium", "bs4", "requests", "zipfile"],
                'includes': ['Rocketupload','paywhirl'],
                'include_msvcr': True,
            },
        },
        executables = [Executable("Rocketupload.py", base=base,icon = "rsc/imgs/LogoBlackIcon.ico")]
        )

My expected result would be to be able to unpack my downloaded zip file.


Solution

  • Try to add "encodings" to the packages list in your setup.py script:

    "packages": ["encodings", "os","time","csv","sys","shutil","re","random","pickle","traceback", "autoit", "ctypes", "threading", "_thread", "configparser", "platform", "subprocess", "queue", "multiprocessing", "psutil", "datetime", "webbrowser","PIL","tkinter","cryptography","abc","selenium", "bs4", "requests", "zipfile"],
    

    Quoting an answer by cx_Freeze's main developer in a quite old issue:

    You need to add the option "--include-modules encodings.cp437" on your command line as this module is loaded dynamically at runtime (so static analysis cannot catch that).

    If you are not using the latest cx_Freeze version (6.0), maybe updating cx_Freeze to this version might be enough to solve the problem.