Branca Python module is unable to find 2 essential json files when running an executable that uses folium

旧巷老猫 提交于 2020-05-14 07:09:06

问题


There is a chance this is still a problem and the Pyinstaller and/or Folium people have no interest in fixing it, but I'll post it again here in case someone out there has discovered a workaround.

I have a program that creates maps, geocodes etc and recently added the folium package to create some interactive maps in html format. I always compile my code using pyinstaller so that others at my company can just use the executable rather than running the python code. If I run my code in an IDE, it loads, runs and performs exactly as expected. However, when I attempt to compile while I have import folium somewhere in my script, I get an error when trying to run the executable that pyinstaller creates.

The error text reads something like this:

Traceback (most recent call last):
File "analysisSuite.py", line 58, in <module>
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "c:\users\natha\appdata\local\programs\python\python36-32\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 631, in exec_module
exec(bytecode, module.__dict__)
File "site-packages\folium\__init__.py", line 8, in <module>
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "c:\users\natha\appdata\local\programs\python\python36-32\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 631, in exec_module
exec(bytecode, module.__dict__)
File "site-packages\branca\__init__.py", line 5, in <module>
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "c:\users\natha\appdata\local\programs\python\python36-32\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 631, in exec_module
exec(bytecode, module.__dict__)
File "site-packages\branca\colormap.py", line 29, in <module>
File "site-packages\pkg_resources\__init__.py", line 1143, in resource_stream
File "site-packages\pkg_resources\__init__.py", line 1390, in get_resource_stream
File "site-packages\pkg_resources\__init__.py", line 1393, in get_resource_string
File "site-packages\pkg_resources\__init__.py", line 1469, in _get
File "c:\users\natha\appdata\local\programs\python\python36-32\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 479, in get_data
with open(path, 'rb') as fp:
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\natha\\AppData\\Local\\Temp\\_MEI309082\\branca\\_cnames.json'
[30956] Failed to execute script analysisSuite

I am still relatively new to Python, so trying to decipher what the issue is by this text is pretty overwhelming. I have no idea if there is a workaround, where I just need to edit a file, add a file or add some parameter to pyinstaller, but perhaps someone else out there can read this and has an idea of what could be causing this problem. Thanks in advance to anyone that has suggestions.

EDIT: The problem seems to be with branca, which is a dependency of folium. It looks for that _cnames.json file which is in the site-packages\branca folder but either doesn't get copied as it should or perhaps I need to somehow identify in my script where it should look for those files and then just manually copy them into a folder that I choose.

ADDITIONAL UPDATE: I've been testing and testing and have determined the heart of the problem. When you run your exe, it gets unpacked in a temp folder. One of the modules within branca is colormap.py In the colormap file, there are essentially three lines that keep branca from loading correctly.

resource_package = __name__
resource_path_schemes = '/_schemes.json'
resource_path_cnames = '/_cnames.json'

So, when the executable gets unpacked in this temp folder and branca tries to load up, because of these above lines, it expects these two files to also be in this temp folder, but of course, they won't be because they're being told to always and only be in the folder where the colormap module lives. The key here is figuring out a way so that the path reference can be relative, so that it doesn't look in the temp folder but also that the reference is dynamic, so that wherever you have your executable, as long as you have those json files present in some folder that it "knows" about, then you'll be good. Now I just need to figure out how to do that.


回答1:


I had the same problem. Pyinstaller could not work with the Python Folium package. I could not get your cx_Freeze solution to work due to issues with Python 3.7 and cx_Freeze but with a day of stress I found a Pyinstaller solution which I am sharing with the community.

Firstly you have to edit these 3 files:

  1. \folium\folium.py

  2. \folium\raster_layers.py

  3. \branca\element.py

Makes the following changes, commenting out the existing ENV line and replacing with the code below:

#ENV = Environment(loader=PackageLoader('folium', 'templates'))
import os, sys
from jinja2 import FileSystemLoader
if getattr(sys, 'frozen', False):
        # we are running in a bundle
    templatedir = sys._MEIPASS
else:
    # we are running in a normal Python environment
    templatedir = os.path.dirname(os.path.abspath(__file__))
ENV = Environment(loader=FileSystemLoader(templatedir + '\\templates'))

Create this spec file in your root folder, obviously your pathex and project name will be different:

# -*- mode: python -*-

block_cipher = None


a = Analysis(['time_punch_map.py'],
         pathex=['C:\\Users\\XXXX\\PycharmProjects\\TimePunchMap'],
         binaries=[],
         datas=[
         (".\\venv\\Lib\\site-packages\\branca\\*.json","branca"),
         (".\\venv\\Lib\\site-packages\\branca\\templates","templates"),
         (".\\venv\\Lib\\site-packages\\folium\\templates","templates"),
         ],
         hiddenimports=[],
         hookspath=[],
         runtime_hooks=[],
         excludes=[],
         win_no_prefer_redirects=False,
         win_private_assemblies=False,
         cipher=block_cipher,
         noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
         cipher=block_cipher)
exe = EXE(pyz,
      a.scripts,
      a.binaries,
      a.zipfiles,
      a.datas,
      [],
      name='time_punch_map',
      debug=False,
      bootloader_ignore_signals=False,
      strip=False,
      upx=True,
      runtime_tmpdir=None,
      console=True )

Finally generate the single exe with this command from the terminal:

pyinstaller time_punch_map.spec



回答2:


I could not get this to work using pyinstaller. I had to instead use cx_Freeze.

pip install cx_Freeze

cx_Freeze requires that a setup.py file is created, typically in the same folder as the main script that is being converted to an exe. My setup.py file looks like this:

import sys
from cx_Freeze import setup, Executable
import os.path
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')

# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {"packages": ["pkg_resources","asyncio","os","pandas","numpy","idna","folium","branca","jinja2","matplotlib"]}

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

options = {
    'build_exe': {
        'include_files':[
            os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tk86t.dll'),
            os.path.join(PYTHON_INSTALL_DIR, 'DLLs', 'tcl86t.dll'),
            # 'C:\\Users\\natha\\AppData\\Local\\Programs\\Python\\Python36-32\\Lib\\site-packages\\branca\\_cnames.json',
            # 'C:\\Users\\natha\\AppData\\Local\\Programs\\Python\\Python36-32\\Lib\\site-packages\\branca\\_schemes.json'
         ],
    },
}

setup(  name = "MyProgram",
        version = "0.1",
        description = "MyProgram that I created",
        options = {"build_exe": build_exe_options},
        executables = [Executable("myProgram.py", base=base)])

Notice I had to add various folium dependencies to the "packages" dictionary, such as branca, asyncio and pkg_resources. Also, I did independent updates for asyncio, pkg_resources and even setuptools using pip - for example: pip install --upgrade setuptools

Once those were in place, I would open a command prompt from the directory where my setup.py file is saved and just type python setup.py build Once this runs, I have a new folder in my directory called build and inside of that is another folder, inside of which is my exe, which ran perfectly. Hope this helps someone else that may encounter this problem.



来源:https://stackoverflow.com/questions/54836440/branca-python-module-is-unable-to-find-2-essential-json-files-when-running-an-ex

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!