17

I am trying to build a python script via PyInstaller. I have used the following commands to configure, generate a spec file, and build:

wget pyinstaller.zip, extracted it, python Configure.py, etc, then:

python pyinstaller/Makespec.py --onefile myscript.py
python pyinstaller/Build.py myscript.spec 

Here is the spec file it generated:

# -*- mode: python -*-
a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), 'icinga.py'],
             pathex=['/home/user/projects/icinga_python/releases/v2.1'])
pyz = PYZ(a.pure)
exe = EXE( pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          name=os.path.join('dist', 'myscript'),
          debug=False,
          strip=False,
          upx=True,
          console=1 )

This built an executable file in dist/ directory. When trying to run this file, I get the following:

Traceback (most recent call last):
  File "<string>", line 12, in <module>
  File "/home/user/projects/myscript/releases/v2.1/pyinstaller/iu.py", line 455, in importHook
    raise ImportError, "No module named %s" % fqname
ImportError: No module named mysql

If I moved this executable into the directory of the actual Python code, it gave different results:

Traceback (most recent call last):
  File "<string>", line 12, in <module>
  File "/home/user/projects/myscript/releases/v2.1/pyinstaller/iu.py", line 436, in importHook
    mod = _self_doimport(nm, ctx, fqname)
  File "/home/user/projects/myscript/releases/v2.1/pyinstaller/iu.py", line 521, in doimport
    exec co in mod.__dict__
  File "CLUSTER/mysql/icingasql.py", line 13, in <module>
    import urllib2
  File "/home/user/projects/myscript/releases/v2.1/pyinstaller/iu.py", line 455, in importHook
    raise ImportError, "No module named %s" % fqname
ImportError: No module named urllib2

In the ... pyinstaller docs I see that --onefile is the option I need/want, but for some reason not everything is being compiled in.

The script is not really including anything fancy, just little quick modules I wrote for sql statements, and parsing certain websites.

Paolo
  • 20,112
  • 21
  • 72
  • 113
Cmag
  • 14,946
  • 25
  • 89
  • 140

3 Answers3

8

This error can ocurre when you have dynamic imports in your code. In that case, pyinstaller don't include those packages in exe file. In that case you can:

  1. Add unused import of those packages in your code
  2. Tell pyinstaller to include it

One file option does not change anything in running your code. If you create --onefile exe all files created by pyinstaller are packed to exe file, and unpacked to local temp every time you run exe.

Zico
  • 127
  • 16
gkocjan
  • 735
  • 7
  • 15
8

The problem is that pyinstaller won't see second level imports. So if you import module A, pyinstaller sees this. But any additional module that is imported in A will not be seen.

There is no need to change anything in your python scripts. You can directly add the missing imports to the spec file. Just add the following in a = Analysis(...):

hiddenimports=["mysql"],

This should be the result:

a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), 'icinga.py'],
         pathex=['/home/user/projects/icinga_python/releases/v2.1'], hiddenimports=["mysql"],)

After that run pyinstaller with the spec file as an argument.

user1251007
  • 15,891
  • 14
  • 50
  • 76
  • 1
    thanx +1! Note: when running pyinstaller afterwards, provide as argument the .spec file, not the .py file! (read question for details, I did not the first time... ;) ) – ntg Nov 07 '17 at 09:10
  • 2
    This is not true. PyInstaller ***WILL*** find secondlevel imports. However sometimes modules are imported dynamically. (The import is done in the code with importlib) and thus PyInstaller is not able to find the module. In those cases the hidden-import clause can solve your problem. Furthermore in most cases you don't need to run Analysis separately and if you add the hidden-imports clause to the pyinstaller command it will find the module. – Dick Kniep Aug 27 '18 at 14:36
2

just gonna add my 2 cents because I encountered the same problem today - 6 years later :D

For Windows:

1) cmd => rightclick => with admin rights
2) Enter in cmd: "pip install pyinstaller"
3) navigate in cmd to the folder of "yourMain.py"
4) Enter in cmd: "pyinstaller --onefile --windowed yourMain.py"

5) If you import other scripts / data in "yourMain.py": 
Manually enter the folder "dist" (gets created - where "yourMain.exe" should be by now), 
and copy your scripts or folder structure there

(e.g. /assets/sounds; /assets/graphics; /scripts; anotherscript.py )

Then I was able to run the exe by double clicking.

Turned out to be pretty easy. What did the trick for me was the "--onefile" and adding my other files to the "dist" folder.

The "--windowed" is just so the python command window won't pop up when you start the exe.

Cribber
  • 2,513
  • 2
  • 21
  • 60
  • Your workaround of manually adding scripts to the dist folder finally worked for me. But still, there should be another solution... – Niels Henkens Apr 30 '19 at 09:32
  • I am trying to generate an installer that will copy my exe to another users system, so manually moving around .py files to the correct directory is certainly not practical. – RufusVS Aug 17 '21 at 18:00