Python2.7+PyInstaller3.3.1打包Flask应用遇ImportError求助
Hey there! I totally get the frustration of guessing hidden dependencies for PyInstaller—been there, done that with Flask apps too. Let's walk through how to decode your error and build a repeatable method for future issues.
First, Let's Break Down Your Current Error
Your traceback gives all the clues you need, let's read it from bottom to top (since the final error is at the end, but the cause is upstream):
Traceback (most recent call last): File "hello.py", line 22, in <module> File "flask/app.py", line 943, in run File "werkzeug/serving.py", line 812, in run_simple File "werkzeug/_reloader.py", line 273, in run_with_reloader File "werkzeug/_reloader.py", line 152, in run File "werkzeug/_reloader.py", line 22, in _iter_module_files File "email/__init__.py", line 79, in __getattr__ ImportError: No module named image
The key lines here are:
File "email/__init__.py", line 79, in __getattr__— This tells you the error is coming from theemailmodule's dynamic import logic, not your code directly.ImportError: No module named image— But wait, you don't useimage! That's because theemailmodule uses lazy/dynamic imports: when code tries to accessemail.image, it attempts to import theemail.imagesubmodule on the fly. PyInstaller's static analysis can't pick up these dynamic imports, so you need to explicitly tell it to includeemail.image, not a standaloneimagemodule.
Your mistake was adding --hiddenimport image—you should be adding --hiddenimport email.image instead.
General Methods to Find Hidden Dependencies
1. Reverse-Engineer the Traceback
This is the most reliable method for issues like yours:
- Start at the
ImportErrorline, then look at the line above it to see which module triggered the import (here,email/__init__.py). - If you can, look at the source code of that module (run
python -c "import email; print(email.__file__)"to find its path). The__getattr__method inemail/__init__.pyis designed to import submodules dynamically when accessed—so when something (in your case, Werkzeug's reloader) tries to accessemail.image, it tries to import that submodule. - The missing dependency is almost always a submodule of the module listed in the line above the ImportError (e.g.,
email.image, notimage).
2. Use PyInstaller's Debug Logs
Run PyInstaller with debug logging enabled to see exactly which modules it's detecting (and missing):
pyinstaller --noupx --log-level DEBUG hello.py
Look for lines like INFO: Analyzing or WARNING: Hidden import not found—these will highlight modules PyInstaller couldn't pick up automatically.
3. Track Imports in Your Local Environment
Use Python's built-in tools to see all modules your app actually imports when running normally:
- Trace module: Run your app with trace mode to log every import:
This will output a list of every module loaded—compare this list to what PyInstaller includes to spot gaps.python -m trace -t hello.py - Check loaded modules: Add this snippet to your
hello.pyright beforeapp.run()to print all loaded modules:
Run your app normally, and you'll see whichimport sys print("Loaded modules:", [m for m in sys.modules if 'email' in m])emailsubmodules are actually used—add those as--hiddenimportflags.
4. Use PyInstaller's Collection Flags (For Lazy Cases)
If you're dealing with a module that has lots of dynamic imports (like Flask or Werkzeug), you can use --collect-all to force PyInstaller to include everything from a package:
pyinstaller --noupx --collect-all email --hiddenimport email.mime.message hello.py
Note: This will add more files than necessary, which might not be ideal for embedded Linux (where space is tight), but it's a quick way to test if you've missed dependencies before trimming down.
Fix for Your Current Issue
Update your PyInstaller command to replace the incorrect image hidden import with email.image:
pyinstaller --noupx --hiddenimport email.mime.message --hiddenimport email.image hello.py
This should resolve the ImportError: No module named image error. If you hit more missing modules, repeat the traceback or debug log method to identify them.
内容的提问来源于stack exchange,提问作者user2413439

