How to include only needed modules in pyinstaller? - python

I'm using pyinstaller to generate an .exe file for my single python file, but the size is more than 30MB and the startup is very slow. From what I have gathered is that pyinstaller by default bundles a lot of stuff that are not needed. Is there a way to make sure that pyinstaller figures out what is only needed and bundles only them? My import section on my script looks like this:
import datetime
import os
import numpy as np
import pandas as pd
import xlsxwriter
from tkinter import *
EDIT:
Or is there also a way to see the list of all modules it has bundled? So I can go through them and exclude the ones I don’t need.

For this you need to create a separate environment, because currently you are reading all the modules you have installed on your computer.
To create environment run commands
1 - if you don't have one, create a requirements.txt file that holds all packages that you are using, you could create one with:
pip freeze > requirements.txt
2 - create env folder:
python -m venv projectName
3 - activate the environment:
source projectName/bin/activate
4 - install them:
pip install -r requirements.txt
alternatively if you know you are using only wxpython you could just pip install wxpython
5 - then finally you can run pyinstaller on your main script with the --path arg as explained in this answer:
pyinstaller --paths projectName/lib/python3.7/site-packages script.py

I ended up using cx_Freeze in the end. It seems to work much better than py2exe or pyinstaller. I wrote setup.py file that looks like this:
import os
import shutil
import sys
from cx_Freeze import setup, Executable
os.environ['TCL_LIBRARY'] = r'C:\bin\Python37-32\tcl\tcl8.6'
os.environ['TK_LIBRARY'] = r'C:\bin\Python37-32\tcl\tk8.6'
__version__ = '1.0.0'
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
include_files = ['am.png']
includes = ['tkinter']
excludes = ['matplotlib', 'sqlite3']
packages = ['numpy', 'pandas', 'xlsxwriter']
setup(
name='TestApp',
description='Test App',
version=__version__,
executables=[Executable('test.py', base=base)],
options = {'build_exe': {
'packages': packages,
'includes': includes,
'include_files': include_files,
'include_msvcr': True,
'excludes': excludes,
}},
)
path = os.path.abspath(os.path.join(os.path.realpath(__file__), os.pardir))
build_path = os.path.join(path, 'build', 'exe.win32-3.7')
shutil.copy(r'C:\bin\Python37-32\DLLs\tcl86t.dll', build_path)
shutil.copy(r'C:\bin\Python37-32\DLLs\tk86t.dll', build_path)
Any then one can either run python setup.py build_exe to generate an executable or python setup.py bdist_msi to generate an installer.

I don't think it can figure that out for you. If there are any specific modules taking a while to load, use the --exclude-module flag to list all the modules you want to exclude.
edit: this answer may have some more helpful info

Related

Get Path to Python File Before Installation

I have a project that includes c++ binaries and python scripts, it's setup such that it should be installed using setuptools. One of the python files is intended to be both used as a script "
python3 script_name.py params
and for it's primary function to be used in other python projects from script_name import function.
The primary function calls a binary which is in a known relative location before the installation (the user is expected to call pip install project_folder). So in order to call the binary I want to get this files location (pre installation)
To get this I used something like
Path(__file__).resolve().parent
however, since the installation moves the file to another folder like ~/.local/... this doesn't work when imported after the installation.
Is there a way to get the original file path, or to make the installation save that path somewhere?
EDIT:
After #sinoroc 's suggestion I tried including the binary as a resource by putting an __init__.py in the build folder and putting
from importlib.resources import files
import build
binary = files(build).joinpath("binary")
in the main init. After that package.binary still gives me a path to my .local/lib and binary.is_file() still returns False
from importlib_resources import files
GenerateHistograms = files("build").joinpath("GenerateHistograms")
gave the same result
Since you are installing your package, you also need to include your C++ binary in the installation. You cannot have a mixed setup. I suggest something like this.
In your setup.py:
from setuptools import setup, find_packages
setup(
name="mypkg",
packages=find_packages(exclude=["tests"]),
package_data={
"mypkg": [
"binary", # relative path to your package directory
]
},
include_package_data=True,
)
Then in your module use pkg_resources:
from pathlib import Path
from pkg_resources import resource_filename
# "binary" is whatever relative path you put in package_data
path_to_binary = Path(resource_filename("mypkg", "binary"))
pkg_resources should be pulled in by setuptools.
EDIT: the recipe above might be a bit out of date; as #sinoroc suggests, using importlib.resources instead of pkg_resources is probably the modern equivalent.
So I managed to solve it in with #sinroc 's approach in the end
In setup.py
package_data={'package':['build/*']}
include_package_data=True
and in the primary __init.py__:
from importlib.resources import files
binary = files("package.build").joinpath("binary")
So I could then from package import binary to get the path
EDIT: Looks like someone also pointed the error in my ways out before I finished ^^

Compiling python project with Cx-Freeze

I'm trying to make and executable from a project I'm working on, to test it on a different computer, without requiring python installed.
I want to use cx freeze to build and exe file, and this is my project tree:
- Project
- objects
blocks.py
enemy.py
item.py
player.py
__init__.py
- scripts
camera.py
chunk_manager.py
__init__.py
- textures
- items
- player
- terrain
- UI
- void
index.py
__init__.py
main.py
settings.py
setup.py
map.json
As you can probably tell, this is a game.
Anyways, what I have done is executing just cxfreeze main.py --target-dir=./ dist and it generated a build directory with a lot of stuff in it.
It generated a linux executable, but thats fine, I want to test if I can make it python-independent first, and I'll solve the .exe part later.
So, I executed the file, and nothing happened. Then I tried running from the terminal and it said that it was missing the camera.py file. I went into the generated directory and I found out that cxfreeze had not copied that file. I tried moving in in there myself, but it did not work.
I started checking for other files, and I found out that only the textures had been copied.
I thought this was just because of the one line command I used, so I am now trying to make a setup.py file (as you can see in the file tree) but I am lost in how to import my custom packages (objects, scripts, and textures) and the files in the same directory as main.py
This is how my setup.py file looks like:
import sys
from cx_Freeze import setup, Executable
options = {
'build_exe': {
'includes': ['camera', 'chunk_manager'], 'path': sys.path + ['scripts']
}
}
setup(
name = "Void Boats",
version = "alpha0.1",
options = options,
executables = [Executable("main.py")])
I am not sure how would I put all the packages in the 'include' section, and I can't find anything on the internet that uses more than a simple file like helloworld.py, and the samples of cxfreeze on their github only show how to import files from one directory. Do I have to move everything into one single package and put all the files in the same 'include'? Or can I have one include for each package I have? Any help would be pretty much appreciated.
Edit: I'm running on ParrotSec 4.9 (Debian based)
Use "python -m setup.py build" on cmd, that will build exe.
Example setup with extra folders:
from cx_Freeze import setup, Executable
import sys
version = "0.0.6"
build_options = {
"packages": [],
"excludes": [],
"build_exe": "X:\\builds\\",
"include_files": ["Sequence_Sample/", "icons/"],
}
base = "Win32GUI" if sys.platform == "win32" else None
executables = [Executable("MainUIController.py", base=base, targetName="pym")]
setup(
name="Python Image Macro Project",
version=version,
description="Image Based macro project.",
options={"build_exe": build_options},
executables=executables,
)
Python 3.8 and later has problem with cx_freeze 6.1 - not copying python dll.
cx_freeze 6.2 is strongly recommended if that's the case.
You'll have to clone cx_freeze and build it, then install it to use 6.2.

Setuptools: how to distribute configuration files without erasing existing ones?

I'm trying to find a nice way to ship default configuration files with my python setuptools project.
For now I'm doing it like this:
from setuptools import setup
setup(
...
data_files = [('/usr/local/etc', ['files/myproject.conf', ...])]
...
)
The problem is that the configuration files are erased if I uninstall my package. Usually, when uninstalling a package on Linux or FreeBSD, the configuration files are not deleted. I think this is good, because sometimes you just want to uninstall a package to reinstall another version, or to install it with other options etc... You don't expect your configurations files to be deleted.
How to achieve the same with setuptools? How to install configurations files only if they don't already exist?
Why can't you? setup.py is also a python script, maybe you can manually add your configuration files to avoid adding them into metadata? Like the following code:
from setuptools import setup
from shutil import copyfile
import os
...
setup(
...
# no data_files option
...
)
if not os.path.exists(configuration_file):
copyfile(configuration, configuration_file)

cx_Freeze is not finding self defined modules in my project when creating exe

I have a project that runs from GUI.py and imports modules I created. Specifically it imports modules from a "Library" package that exists in the same directory as GUI.py. I want to freeze the scripts with cx_Freeze to create a windows executable, and I can create the exe, but when I try to run it, I get: "ImportError: No module named Library."
I see in the output that all the modules that I import from Library aren't imported. Here's what my setup.py looks like:
import sys, os
from cx_Freeze import setup, Executable
build_exe_options = {"packages":['Libary', 'Graphs', 'os'],
"includes":["tkinter", "csv", "subprocess", "datetime", "shutil", "random", "Library", "Graphs"],
"include_files": ['GUI','HTML','Users','Tests','E.icns', 'Graphs'],
}
base = None
exe = None
if sys.platform == "win32":
exe = Executable(
script="GUI.py",
initScript = None,
base = "Win32GUI",
targetDir = r"built",
targetName = "GUI.exe",
compress = True,
copyDependentFiles = True,
appendScriptToExe = False,
appendScriptToLibrary = False,
icon = None
)
base = "Win32GUI"
setup( name = "MrProj",
version = "2.0",
description = "My project",
options = {"build.exe": build_exe_options},
#executables = [Executable("GUI.py", base=base)]
executables = [exe]
)
I've tried everything that I could find in StackOverflow and made a lot of fixes based upon similar problems people were having. However, no matter what I do, I can't seem to get cx_freeze to import my modules in Library.
My setup.py is in the same directory as GUI.py and the Library directory.
I'm running it on a Windows 7 Laptop with cx_Freeze-4.3.3.
I have python 3.4 installed.
Any help would be a godsend, thank you very much!
If Library (funny name by the way) in packages doesn't work you could try as a workaround to put it in the includes list. For this to work you maybe have to explicitly include every single submodule like:
includes = ['Library', 'Library.submodule1', 'Library.sub2', ...]
For the include_files you have to add each file with full (relative) path. Directories don't work.
You could of course make use of os.listdir() or the glob module to append paths to your include_files like this:
from glob import glob
...
include_files = ['GUI.py','HTML','Users','E.icns', 'Graphs'],
include_files += glob('Tests/*/*')
...
In some cases something like sys.path.insert(0, '.') or even sys.path.insert(0, '..') can help.

cx_Freeze does not include .py files from subdirectories

I'm building a program with Python 3.3, Pyside and lxml amongst others. cx_Freeze was working like a charm until I starting sorting my modules into subdirectories for neatness. cx_Freeze works but reports the modules as missing and the output .exe file fails to load, but there are no issues opening the program with the .py file.
My folder structure is simple and only has 2 sub-folders called parsers and constants.
The cx_Freeze documentation allows for adding a path variable, but this doesn't search for the modules. All help appreciated.
import sys
from cx_Freeze import setup,Executable
includefiles = ['open.png', 'appicon.png']
includes = []
excludes = ['Tkinter']
packages = ['lxml._elementpath','lxml.etree']
build_exe_options = {'excludes':excludes,'packages':packages,'include_files':includefiles}
base = None
if sys.platform == "win32":
base = "Win32GUI"
setup(
name = 'Config-Sheets',
version = '0.1',
description = 'Convert system configuration into .XLSX files',
author = 'ZZZ',
author_email = 'XXX#YYY.com',
options = {'build_exe': build_exe_options },
executables = [Executable('main.py', base=base)]
)
Results in and an exe that can't run.
Missing modules:
...
? parsers.parsercelerra imported from main__main__
? parsers.parserVPLEX imported from main__main__
? parsers.parserrain imported from main__main__
You should be able to include your packages very simply:
packages = ['lxml._elementpath','lxml.etree', 'parsers', 'constants']
Of course the directories must contain __init__.py in order to be considered packages.
If this doesn't achieve the desired effect then it would be useful to see the import mechanism used in main.py to pull these files in. If it's doing anything other than a straightforward import (or from x import) this can give cx_Freeze problems finding it.
You should include your .py files in the setup.py script. If the file is not a part of Python itself, cx_freeze needs to know that you want those files included. You should add your extra .py files with their paths to the includefiles list. For instance:
import sys
from cx_Freeze import setup,Executable
includefiles = ['open.png', 'appicon.png', 'parsers\xtra_file1.py','constants\xtra_file2.py']
includes = []
excludes = ['Tkinter']
packages = ['lxml._elementpath','lxml.etree']
build_exe_options = {'excludes':excludes,'packages':packages,'include_files':includefiles}
...
Hope this helped.

Categories