Summary
A packaged PyInstaller + pywebview application failed at runtime with the error “You must have pythonnet installed” even though pythonnet was installed in the development environment. The root issue was that PyInstaller did not bundle pythonnet’s CLR loader, causing pywebview’s .NET backend to fail during initialization.
Root Cause
The failure occurred because pythonnet relies on the clr module, which PyInstaller does not automatically detect. Even when using --hidden-import=clr, PyInstaller often misses:
- pythonnet’s native DLLs (e.g.,
Python.Runtime.dll) - Dependent .NET assemblies
- pythonnet’s loader hooks, which pywebview expects to be present at runtime
As a result, the packaged executable cannot import clr, and pywebview raises the pythonnet‑missing exception.
Why This Happens in Real Systems
This class of failure is common because:
- PyInstaller’s module graph cannot detect dynamic imports, especially those performed inside C#/.NET interop layers.
- pythonnet loads assemblies at runtime, which PyInstaller does not automatically track.
- pywebview selects a GUI backend dynamically, and the .NET backend silently fails if pythonnet is missing.
- Development environments mask the issue, because pythonnet is available globally, but the frozen app has no access to it.
Real-World Impact
When pythonnet is not bundled correctly:
- Applications fail only after packaging, not during development.
- GUI initialization crashes before any window appears.
- Debugging becomes difficult because the error message misleadingly suggests pythonnet is not installed.
- Teams waste time reinstalling packages instead of fixing the bundling issue.
Example or Code (if necessary and relevant)
A minimal PyInstaller spec file that correctly bundles pythonnet typically requires manual inclusion:
# test.spec
# PyInstaller spec file with pythonnet support
block_cipher = None
a = Analysis(
['test.py'],
hiddenimports=['clr', 'pythonnet'],
)
binaries = [
('Python.Runtime.dll', 'path/to/site-packages/pythonnet/')
]
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(pyz, a.scripts, a.binaries + binaries, a.zipfiles, a.datas, name='test', console=False)
How Senior Engineers Fix It
Experienced engineers solve this by ensuring pythonnet’s native components are explicitly bundled:
- Add
--collect-binaries=pythonnetto PyInstaller. - Add
--collect-data=pythonnetto include metadata. - Add
--hidden-import=clrand--hidden-import=pythonnet. - Verify that
Python.Runtime.dllis present in the finaldist/directory. - Switch pywebview to a different backend (e.g., GTK, Qt) if .NET is not required.
A typical working command:
pyinstaller --onefile --windowed \
--hidden-import=clr \
--hidden-import=pythonnet \
--collect-binaries=pythonnet \
--collect-data=pythonnet \
test.py
Why Juniors Miss It
Less experienced developers often overlook this because:
- They assume pip-installed packages automatically bundle correctly, which is not true for native modules.
- They do not realize pythonnet is not pure Python and requires DLLs.
- They trust PyInstaller’s default behavior and do not inspect the
dist/output. - They interpret the error literally (“pythonnet not installed”) instead of recognizing it as a packaging failure.
Would you like a fully working PyInstaller spec file tailored to your environment?