SonGoku
SonGoku

Reputation: 61

"distribution was not found and is required by the application" when using pyinstaller with setuptools

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

Answers (2)

PJ127
PJ127

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

user20124386
user20124386

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

Related Questions