Reputation: 61
I am trying to make an executable from a setuptools distribution, I have the following structure:
TEST-DIST
├── app.spec
├── my_package
│ ├── __init__.py
│ ├── __main__.py
│ ├── script1.py
│ └── script2.py
└── setup.py
Setup.py:
from importlib.metadata import entry_points
import setuptools
setuptools.setup(
name="testapp",
version="0.0.1",
author="Example Author",
author_email="[email protected]",
description="A small example package",
url="https://github.com/pypa/sampleproject",
project_urls={
"Bug Tracker": "https://github.com/pypa/sampleproject/issues",
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
entry_points={'console_scripts': ["mytestscript1=my_package.script1:main", "mytestscript2=my_package.script2:main"]},
python_requires=">=3.6"
)
app.spec that uses this script to work with setuptools
block_cipher = None
def Entrypoint(dist, group, name, **kwargs):
import pkg_resources
# get toplevel packages of distribution from metadata
def get_toplevel(dist):
distribution = pkg_resources.get_distribution(dist)
if distribution.has_metadata('top_level.txt'):
return list(distribution.get_metadata('top_level.txt').split())
else:
return []
kwargs.setdefault('hiddenimports', [])
packages = []
for distribution in kwargs['hiddenimports']:
packages += get_toplevel(distribution)
kwargs.setdefault('pathex', [])
# get the entry point
ep = pkg_resources.get_entry_info(dist, group, name)
# insert path of the egg at the verify front of the search path
kwargs['pathex'] = [ep.dist.location] + kwargs['pathex']
# script name must not be a valid module name to avoid name clashes on import
script_path = os.path.join(workpath, name + '-script.py')
print("creating script for entry point", dist, group, name)
with open(script_path, 'w') as fh:
print("import", ep.module_name, file=fh)
print("%s.%s()" % (ep.module_name, '.'.join(ep.attrs)), file=fh)
for package in packages:
print("import", package, file=fh)
return Analysis(
[script_path] + kwargs.get('scripts', []),
**kwargs
)
a = Entrypoint("testapp", 'console_scripts', 'mytestscript1')
pyz = PYZ(a.pure, a.zipped_data)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='mytestscript1')
I first build the package using python3 -m build
, Then I execute pyinstaller pyinstaller app.spec
, Which results the following error:
27 INFO: PyInstaller: 4.10
27 INFO: Python: 3.8.10
34 INFO: Platform: Linux-5.10.60.1-microsoft-standard-WSL2-x86_64-with-glibc2.29
35 INFO: UPX is not available.
Traceback (most recent call last):
File "/usr/local/bin/pyinstaller", line 8, in <module>
sys.exit(run())
File "/home/admin/.local/lib/python3.8/site-packages/PyInstaller/__main__.py", line 124, in run
run_build(pyi_config, spec_file, **vars(args))
File "/home/admin/.local/lib/python3.8/site-packages/PyInstaller/__main__.py", line 58, in run_build
PyInstaller.building.build_main.main(pyi_config, spec_file, **kwargs)
File "/home/admin/.local/lib/python3.8/site-packages/PyInstaller/building/build_main.py", line 803, in main
build(specfile, distpath, workpath, clean_build)
File "/home/admin/.local/lib/python3.8/site-packages/PyInstaller/building/build_main.py", line 725, in build
exec(code, spec_namespace)
File "app.spec", line 42, in <module>
a = Entrypoint("testapp", 'console_scripts', 'mytestscript1', datas=[('testapp.egg-info/*','testapp.egg-info')])
File "app.spec", line 25, in Entrypoint
ep = pkg_resources.get_entry_info(dist, group, name)
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 500, in get_entry_info
return get_distribution(dist).get_entry_info(group, name)
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 482, in get_distribution
dist = get_provider(dist)
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 358, in get_provider
return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 901, in require
needed = self.resolve(parse_requirements(requirements))
File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 787, in resolve
raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'testapp' distribution was not found and is required by the application
The exception is invoked because pkg_resources.get_entry_info
doesn't find my distribution when it is executed from pyinstaller, but when I run it from any normal script in the root it can be found without problems:
>>> import pkg_resources
>>> pkg_resources.get_entry_info("testapp", 'console_scripts', 'mytestscript1')
EntryPoint.parse('mytestscript1 = my_package.script1:main')
None of the solutions here helped!
Upvotes: 4
Views: 10780
Reputation: 1258
That may not help anybody, but my repo was using pyinstaller from my main python distribution, until I installed pyinstaller in my venv. Then I got:
where pyinstaller
C:\dev\code\t3-revue\.venv\small\Scripts\pyinstaller.exe
C:\Users\xxx\AppData\Local\Programs\Python\Python311\Scripts\pyinstaller.exe
When it used the one from my venv, from which I was issuing the command, it worked:
$ pwd
/c/dev/code/t3-revue
$ cls; pyinstaller config/installer/eyebrain.spec -y
-> no error
Hope that helps.
Upvotes: 0
Reputation: 1
I think we might be having the same problem. I have the same file structure as you, with the package name being named something different than the directory where the code is (package is named testapp
but directory is named my_package
). I noticed that when I'd pip install
my package, in site-packages
the name of the egg-info
directory (or dist-info
perhaps in your case) was testapp_etc_.egg-info
and the source code directory was my_package
. I manually changed it to mypackage_etc_.egg-info
and pkg_resources
was able to find the entry point.
I should also mention I did not require any changes to the .spec
file. I stared at that Recipe for setuptools for a while yesterday and gave up on it, I just used my regular command line arguments when using pyinstaller
. Lastly, manually changing the name of the directory is not the right solution here, I just did it to see if that was the issue. The actual solution would be to change the name of the package argument in setup.setup()
.
Upvotes: 0