pythonexepyinstallerflask-socketiocefpython

Bundle CEFpython on PyInstaller with --onefile option


I have an app where i had two executables: A Flask-SocketIO-Server and CefPython browser. I bundled the two executables with PyInstaller. The Flask-Server with --onefile option and the cefpython with --onedir option, because i couldnt make it with --onefile. Now i decided to have only executable for both codes (Flask and CEFpython), so my flask server has code to run the CEF graphical user interface:

if __name__ == '__main__':

    if len(sys.argv) > 1 and sys.argv[1] == 'dev':
        print "Running Flask-SocketIO on dev mode"
    else:
        print "Running Flask-SocketIO on production mode"
        path = os.getcwd()
        gui_path = path + '\\display_react\\display_react.exe'
        print 'Running Graphical User Interface...'
        thread.start_new_thread(display_react.main, ())  # Baterias
        print 'Initializing server'


    socketio.run(app, debug=False)

The code works fine, but when i try to bundle this code with PyInstaller with --onefile option, the generated executable doesnt work cause some of CEF dependencies. Here the errors when running Pyinstaller:

Running Flask-SocketIO on production mode Running Graphical User Interface... Initializing server [wxpython.py] CEF Python 57.1 [wxpython.py] Python 2.7.14 64bit [wxpython.py] wxPython 4.0.1 msw (phoenix) [0727/125110.576:ERROR:main_delegate.cc(684)] Could not load locale pak for en-US [0727/125110.576:ERROR:main_delegate.cc(691)] Could not load cef.pak [0727/125110.578:ERROR:main_delegate.cc(708)] Could not load cef_100_percent.pak [0727/125110.582:ERROR:main_delegate.cc(717)] Could not load cef_200_percent.pak [0727/125110.582:ERROR:main_delegate.cc(726)] Could not load cef_extensions.pak [0727/125110.648:ERROR:content_client.cc(269)] No data resource available for id 20418 [0727/125110.648:ERROR:content_client.cc(269)] No data resource available for id 20419 [0727/125110.650:ERROR:content_client.cc(269)] No data resource available for id 20420 [0727/125110.655:ERROR:content_client.cc(269)] No data resource available for id 20421 [0727/125110.656:ERROR:content_client.cc(269)] No data resource available for id 20422 [0727/125110.656:ERROR:content_client.cc(269)] No data resource available for id 20417 [0727/125110.680:ERROR:extension_system.cc(72)] Failed to parse extension manifest. C:\Users\Ricardo\AppData\Local\Temp_MEI95~1\display_react.py:118: wxPyDeprecationWarning: Call to deprecated item EmptyIcon. Use :class:Icon instead

Here the .spec file i am using:

# -*- mode: python -*-

block_cipher = None

def get_cefpython_path():
    import cefpython3 as cefpython

    path = os.path.dirname(cefpython.__file__)
    return "%s%s" % (path, os.sep)

cefp = get_cefpython_path()


a = Analysis(['server.py'],
             pathex=['C:\\Users\\Ricardo\\addvolt-scanning-tool\\backend'],
             binaries=[],
             datas=[('PCANBasic.dll', '.'), ('o.ico', '.')], #some dlls i need for flask
             hiddenimports=['engineio.async_gevent'], #engineio hidden import for Flask usage
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas + [('locales/en-US.pak', '%s/locales/en-US.pak' % cefp, 'DATA')], # my try to fix that missing dependencies
          name='server',
          debug=False,
          strip=False,
          upx=True,
          runtime_tmpdir=None,
          console=True )

EDIT: SOLVED

Thanks to @cztomczak i got this working. The problem was not on PyInstaller, but on the way wxpython.py was looking for the locale, resources and subprocess stuff. Although all files were on the 'temp/dir/_MEIxxx', the wxpython was looking for these files on the directory of the executable. So the way i got to inform the code to look for these files on temp directory was:

dir_temp = tempfile.gettempdir()
files = []
for i in os.listdir(dir_temp):
    if os.path.isdir(os.path.join(dir_temp,i)) and '_MEI' in i:
        files.append(i)
dir_temp = dir_temp + str(files[0])
dir_temp = os.path.join(dir_temp, str(files[0]))
dir_temp_locale = os.path.join(dir_temp, 'locales')
dir_temp_subprocess = os.path.join(dir_temp_subprocess, 'subprocess.exe')

print dir_temp
dir_temp = dir_temp.replace("\\", "\\\\")
print dir_temp
print dir_temp_locale
dir_temp_locale = dir_temp_locale.replace("\\", "\\\\")
print dir_temp_locale
dir_temp_supbprocess = dir_temp_subprocess.replace("\\", "\\\\")
print dir_temp_subprocess

...

settings = {'auto_zooming': '-2.5', 'locales_dir_path': dir_temp_locale, 'resources_dir_path': dir_temp, 'browser_subprocess_path': dir_temp_subprocess}

i had to do this because the name of the created folder on temp (_MEIxxxx) is always changing. And probably i will have problems in the future because if the App crashes, the _MEIxx folder will not be deleted and if i try to re-run the executable, this piece of code will have two _MEI folders and possibily will not work at all until someone clean the temp directory.

So, resuming... To bundle in onefile the app: - Paste the hook-cefpython3.py (available on the package) on Python27/envs/libs/site-package/Pyinstaller/hooks - Run Pyinstaller with --onefile options - Tell the cefpython code where locale, resource and subprocess are (locale_dir_path, resource_dir_path, browser_subprocess_path)


Solution

  • I guess the errors you got are because your spec file didn't include all necessary CEF binary files. There is an official pyinstaller example that you can use and modify to use --onefile option: https://github.com/cztomczak/cefpython/blob/master/examples/pyinstaller/README-pyinstaller.md