What are PyInstaller "hidden imports" — and why do only dynamic imports break?
If you've ever packaged a Python desktop app with PyInstaller, you may have run into this: the app runs perfectly from source, but the frozen executable throws ModuleNotFoundError — and only when you exercise one particular feature. It doesn't crash on startup. It crashes three clicks deep, in a code path nobody happened to test right after the build. This post breaks down why that happens and…
PyInstaller is a tool that bundles Python scripts and their dependencies into a single executable, allowing end users to run the app without needing a Python environment. The tool analyzes the source code to determine which modules are needed, but it doesn't execute the code to do this. As a result, any module that is only referenced dynamically during runtime may not be detected, leading to hidden import issues.
These hidden imports usually occur in three scenarios: code that constructs import strings at runtime, dynamic scanning for plugins, or modules loaded by third-party packages with conditional loading. These dynamic imports or function-scoped imports can lead to ModuleNotFoundError when the app is frozen into an executable. The issue doesn't always manifest during the development process, as the code may still run, displaying the wrong value or triggering a fallback path rather than crashing.
To address this issue, PyInstaller offers a few solutions: using the --hidden-import command line argument, specifying the hiddenimports argument in the .spec file, or writing a dedicated hook file for third-party packages. By explicitly listing all potentially hidden imports, developers can ensure that their applications run correctly without unexpected ModuleNotFoundError exceptions, preventing silent bugs that only reveal themselves during runtime.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.