当 运行 使用 folium 的可执行文件时,Branca Python 模块无法找到 2 个基本 json 文件

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

这可能仍然是一个问题,Pyinstaller and/or Folium 的人没有兴趣修复它,但我会 post 在这里再次解决它,以防有人发现解决方法。

我有一个创建地图、地理编码等的程序,最近添加了 folium 包来创建一些 html 格式的交互式地图。我总是使用 pyinstaller 编译我的代码,这样我公司的其他人就可以使用可执行文件而不是 运行 宁 python 代码。如果我 运行 我的代码在 IDE 中,它会加载 运行 并完全按预期执行。但是,当我在脚本中某处有 import folium 时尝试编译时,在尝试 运行 pyinstaller 创建的可执行文件时出现错误。

错误文本内容如下:

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

我对 Python 还是比较陌生,所以试图通过这篇文章来破解问题是非常困难的。我不知道是否有解决方法,我只需要编辑文件、添加文件或向 pyinstaller 添加一些参数,但也许其他人可以阅读此内容并了解可能导致此问题的原因。提前感谢任何有建议的人。

编辑:问题似乎与 branca 有关,它是 folium 的依赖项。它会查找 site-packages\branca 文件夹中的那个 _cnames.json 文件,但要么没有按应有的方式复制,要么我可能需要以某种方式在我的脚本中确定它应该在哪里查找这些文件,然后就可以了手动将它们复制到我选择的文件夹中。

其他更新:我一直在测试和测试并确定了问题的核心。当您 运行 您的 exe 文件时,它会解压到一个临时文件夹中。 branca 中的模块之一是 colormap.pycolormap 文件中,基本上有三行阻止 branca 正确加载。

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

因此,当可执行文件在此临时文件夹中解压缩并且 branca 尝试加载时,由于以上这些行,它希望这两个文件也位于此临时文件夹中,但当然,它们不会是因为他们被告知始终且仅位于颜色图模块所在的文件夹中。这里的关键是找出一种方法,使路径引用可以是相对的,这样它就不会在临时文件夹中查找,而且引用是动态的,这样无论你有可执行文件,只要你有那些json 文件存在于它 "knows" 的某个文件夹中,那么你会很好。现在我只需要弄清楚该怎么做。

我无法使用 pyinstaller 让它工作。我不得不改用 cx_Freeze。

pip install cx_Freeze

cx_Freeze 需要创建一个 setup.py 文件,通常与正在转换为 exe 的主脚本位于同一文件夹中。我的 setup.py 文件如下所示:

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)])

请注意,我必须向 "packages" 字典中添加各种 folium 依赖项,例如 b运行ca、asyncio 和 pkg_resources。此外,我使用 pip 对 asyncio、pkg_resources 甚至 setuptools 进行了独立更新——例如: pip install --upgrade setuptools

一旦这些就绪,我将从保存 setup.py 文件的目录打开命令提示符,然后键入 python setup.py build 运行后,我的目录中有一个名为 build 的新文件夹,里面是另一个文件夹,里面是我的 exe,运行 完美。希望这对可能遇到此问题的其他人有所帮助。

我遇到了同样的问题。 Pyinstaller 无法使用 Python Folium 包。由于 Python 3.7 和 cx_Freeze 的问题,我无法让您的 cx_Freeze 解决方案工作,但由于一天的压力,我找到了一个 Pyinstaller 解决方案,我正在与社区分享。

首先你必须编辑这 3 个文件:

  1. \folium\folium.py

  2. \folium\raster_layers.py

  3. \branca\element.py

进行以下更改,注释掉现有的 ENV 行并替换为以下代码:

#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'))

在您的根文件夹中创建此 spec 文件,显然您的 pathex 和项目名称将不同:

# -*- 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 )

最终从终端使用此命令生成单个 exe:

pyinstaller time_punch_map.spec

使用 pyinstaller,它可以使用上述技巧。如果我们需要创建文件夹,那么可以使用这个脚本文件。

import platform
block_cipher = None
a = Analysis(['Test_Beta.py'],
pathex=['C:\Old desktop\test\esky\fileserver\test11'],
binaries=[(winsparkle, '.')],
datas=[
         ("C:\Users\kv\AppData\Local\Continuum\Anaconda3\Lib\site-packages\branca\*.json","branca"),
         ("C:\Users\kv\AppData\Local\Continuum\Anaconda3\Lib\site-packages\branca\templates","templates"),
         ("C:\Users\kv\AppData\Local\Continuum\Anaconda3\Lib\site-packages\folium\templates","templates"),
         ],
         hiddenimports=[],
         hookspath=[],
         runtime_hooks=[],
         excludes=['scipy', 'zmq', '_gtkagg', '_tkagg', 'bsddb', 'curses', 'pywin.debugger', 'pywin.debugger.dbgcon', 'pywin.dialogs', 'tcl', 'Tkconstants', 'Tkinter'],
         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,
      [],
      exclude_binaries=True,
      name='Outview',
      debug=False,
      strip=False,
      upx=True,
      console=False )

coll = COLLECT(exe,
           a.binaries,
           a.zipfiles,
           a.datas,
           strip=False,
           upx=True,
           name='Outview')