working with pyinstaller
Steps to reproduce
test.py:
from py_mini_racer import MiniRacer
mr = MiniRacer()
run pyinstaller test.py then generated test.exe, it happens that:
Traceback (most recent call last):
File "test.py", line 2, in <module>
File "py_mini_racer\_mini_racer.py", line 56, in __init__
File "py_mini_racer\_dll.py", line 273, in init_mini_racer
File "contextlib.py", line 137, in __enter__
File "py_mini_racer\_dll.py", line 237, in _open_dll
File "py_mini_racer\_dll.py", line 217, in _check_path
py_mini_racer._dll.LibNotFoundError: Native library or dependency not available at D:\test\dist\test\_internal\mini_racer.dll
Expected behavior
no error and no missing mini_racer.dll
Actual behavior
as above
System configuration
win11 PyMiniRacer version: 0.12.4 Python version: python 3.12
Current solution
when i use --collect-binaries py_mini_racer with pyinstaller, there is a mini_racer.dll in .\dist\test\_internal\py_mini_racer, but py_mini_racer will find in .\dist\test\_internal and therefore miss it.
My current solution is using
--add-data D:\myfiles\softwares\miniconda\envs\tmp\Lib\site-packages\py_mini_racer\mini_racer.dll:.
--add-data D:\myfiles\softwares\miniconda\envs\tmp\Lib\site-packages\py_mini_racer\icudtl.dat:.
--add-data D:\myfiles\softwares\miniconda\envs\tmp\Lib\site-packages\py_mini_racer\snapshot_blob.bin:.
with pyinstaller and it will place these files under .\dist\test\_internal and everything will be ok.
Hi!
I don't have a Windows environment to reproduce this issue in but if you want to send a PR I can take a look!
I wonder what the cause is... There is legacy logic in src/py_mini_racer/_dll.py in _open_dll for PyInstaller which I've never been able to test (see the logic related to _MEIPASS which is a PyInstaller thing). I wonder, if you simply remove all logic related to _MEIPASS, does it start working?
Thanks for reply, I test it locally on my windows 11 and here is the result. test.py
from py_mini_racer import MiniRacer
mr = MiniRacer()
run pyinstaller test.py in terminal
Traceback (most recent call last):
File "test.py", line 3, in <module>
File "py_mini_racer\_mini_racer.py", line 56, in __init__
File "py_mini_racer\_dll.py", line 278, in init_mini_racer
File "contextlib.py", line 137, in __enter__
File "py_mini_racer\_dll.py", line 242, in _open_dll
File "py_mini_racer\_dll.py", line 217, in _check_path
py_mini_racer._dll.LibNotFoundError: Native library or dependency not available at D:\myfiles\projects\python\dist\test\_internal\py_mini_racer\mini_racer.dll
[PYI-13040:ERROR] Failed to execute script 'test' due to unhandled exception!
so pyinstaller won't bundle mini_racer.dll and other dependent data files such as icudtl.dat and snapshot_blob.bin.
so we should run pyinstaller test.py --collect-data py_mini_racer
then it will place icudtl.dat, mini_racer.dll, py.typed(0 byte, probably no use) and snapshot_blob.bin in _internal/py_mini_racer directory.
while the code snippet in src/py_mini_racer/_dll.py
@contextmanager
def _open_dll(flags: Iterable[str]) -> Iterator[ctypes.CDLL]:
dll_filename = _get_lib_filename("mini_racer")
with ExitStack() as exit_stack:
# Find the dll and its external dependency files:
meipass = getattr(sys, "_MEIPASS", None)
if meipass is not None:
# We are running under PyInstaller
dll_path = pathjoin(meipass, dll_filename)
icu_data_path = pathjoin(meipass, _ICU_DATA_FILENAME)
snapshot_path = pathjoin(meipass, _SNAPSHOT_FILENAME)
else:
dll_path = _open_resource_file(dll_filename, exit_stack)
icu_data_path = _open_resource_file(_ICU_DATA_FILENAME, exit_stack)
snapshot_path = _open_resource_file(_SNAPSHOT_FILENAME, exit_stack)
will have meipass variable's value be _internal, so it misses py_mini_racer, which is the module name. So it's simple to resolve, just like this.
@contextmanager
def _open_dll(flags: Iterable[str]) -> Iterator[ctypes.CDLL]:
dll_filename = _get_lib_filename("mini_racer")
with ExitStack() as exit_stack:
# Find the dll and its external dependency files:
meipass = getattr(sys, "_MEIPASS", None)
module_name = 'py_mini_racer'
if meipass is not None:
# We are running under PyInstaller
dll_path = pathjoin(meipass, module_name, dll_filename)
icu_data_path = pathjoin(meipass, module_name, _ICU_DATA_FILENAME)
snapshot_path = pathjoin(meipass, module_name, _SNAPSHOT_FILENAME)
else:
dll_path = _open_resource_file(dll_filename, exit_stack)
icu_data_path = _open_resource_file(_ICU_DATA_FILENAME, exit_stack)
snapshot_path = _open_resource_file(_SNAPSHOT_FILENAME, exit_stack)
then it will find the dependent files in the right path. Just a small fix, hope to solve soon and notify me at once so I can test it for you.
And also you can explain about how to work with pyinstaller in the documentation, thank you for your work!