I've begun using Pyinstaller over Py2Exe. However I've rather quickly run into a problem. How do I exclude modules that I don't want, and how do I view the ones that are getting included into the single executable file?
I can remove some pyd
and dll
files from the DLL folder in my Python installation so Pyinstaller doesn't find and therefore doesn't include them. I don't really want to be doing that with all the modules as it will get quite arduous.
I did try edit the spec file that Pyinstaller makes.
a.binaries - [('ssl','pydoc',)],
But the size of the file remained the same so I conclude that didn't work.
So how can I see what modules Pyinstaller are including and how do I exclude those that I do not want?
Many of these types of problems can be resolved by using the --hidden-import PyInstaller CLI option. This tells PyInstaller to include a module or package even if it doesn't automatically detect it.
PyInstaller does not include libraries that should exist in any installation of this OS. For example in GNU/Linux, it does not bundle any file from /lib or /usr/lib , assuming these will be found in every system.
PyInstaller looks for UPX on the execution path or the path specified with the --upx-dir option. If UPX exists, PyInstaller applies it to the final executable, unless the --noupx option was given. UPX has been used with PyInstaller output often, usually with no problems.
The spec file tells PyInstaller how to process your script. It encodes the script names and most of the options you give to the pyinstaller command. The spec file is actually executable Python code. PyInstaller builds the app by executing the contents of the spec file.
Just to summarise the options here as I use them.
PyInstaller TOC's - are, as the documentation says:
A TOC appears to be a list of tuples of the form (name, path, typecode). In fact, it's an ordered set, not a list. A TOC contains no duplicates, where uniqueness is based on name only.
In otherwords, simply:
a_toc = [('uname1','/path/info','BINARY'),('uname2','/path/to','EXTENSION')...]
So, in your .spec file - once you've got the Analysis results of the script - you can then further modify the respective TOC's by either:
For specific files/modules use the difference (-) and intersection (+) operations to modify a TOC. *
For adding/removing lists of files/modules iterate over the the TOC and compare with pattern matching code.
(* As an aside, for the difference to work it seems you must explicitly cast to TOC()
and note that since it is only the name that uniquely defines the element of the set, you only need to specify that - hence ('sqlite3', None, None)
etc.)
An illustrative example (taken from a .spec file) is below where - for better or worse - I remove all references to scipy, IPython and zmq; delete specific sqlite, tcl/tk and ssl .DLL's; insert a missing opencv .DLL; and finally remove all data folders found apart from matplotlib ones...
Whether the resulting Pyinstaller .exe will then work when an .pyc file tries to load an expected .DLL is moot:-/
# Manually remove entire packages...
a.binaries = [x for x in a.binaries if not x[0].startswith("scipy")]
a.binaries = [x for x in a.binaries if not x[0].startswith("IPython")]
a.binaries = [x for x in a.binaries if not x[0].startswith("zmq")]
# Target remove specific ones...
a.binaries = a.binaries - TOC([
('sqlite3.dll', None, None),
('tcl85.dll', None, None),
('tk85.dll', None, None),
('_sqlite3', None, None),
('_ssl', None, None),
('_tkinter', None, None)])
# Add a single missing dll...
a.binaries = a.binaries + [
('opencv_ffmpeg245_64.dll', 'C:\\Python27\\opencv_ffmpeg245_64.dll', 'BINARY')]
# Delete everything bar matplotlib data...
a.datas = [x for x in a.datas if
os.path.dirname(x[1]).startswith("C:\\Python27\\Lib\\site-packages\\matplotlib")]
Although better solutions may have been given but there is one more method: You can use the '--exclude-module' attribute with the 'pyinstaller' command but this method is quite lengthy when you have to exclude many modules.
To make the work easier, you can write a batch script file with all the libraries worth skipping and use it again and again.
Something like this:
@echo off
pyinstaller --onefile a.py --exclude-module matplotlib ^
--exclude-module scipy ^
--exclude-module setuptools ^
--exclude-module hook ^
--exclude-module distutils ^
--exclude-module site ^
--exclude-module hooks ^
--exclude-module tornado ^
--exclude-module PIL ^
--exclude-module PyQt4 ^
--exclude-module PyQt5 ^
--exclude-module pydoc ^
--exclude-module pythoncom ^
--exclude-module pytz ^
--exclude-module pywintypes ^
--exclude-module sqlite3 ^
--exclude-module pyz ^
--exclude-module pandas ^
--exclude-module sklearn ^
--exclude-module scapy ^
--exclude-module scrapy ^
--exclude-module sympy ^
--exclude-module kivy ^
--exclude-module pyramid ^
--exclude-module opencv ^
--exclude-module tensorflow ^
--exclude-module pipenv ^
--exclude-module pattern ^
--exclude-module mechanize ^
--exclude-module beautifulsoup4 ^
--exclude-module requests ^
--exclude-module wxPython ^
--exclude-module pygi ^
--exclude-module pillow ^
--exclude-module pygame ^
--exclude-module pyglet ^
--exclude-module flask ^
--exclude-module django ^
--exclude-module pylint ^
--exclude-module pytube ^
--exclude-module odfpy ^
--exclude-module mccabe ^
--exclude-module pilkit ^
--exclude-module six ^
--exclude-module wrapt ^
--exclude-module astroid ^
--exclude-module isort
Or you can always use a new python installation, just change the path of the new python installation while installing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With