Recipe Setuptools Entry Point

Martin Stejskal edited this page May 11, 2018 · 13 revisions

Packaging from setuptools entry point

setuptools/pkg_resources have a feature called Automatic Script Creation. If your Python package is using this feature to create console or gui scripts, you will have a hard time packaging this with PyInstaller. The reason for this is that the generated script basically looks like this one:

from pkg_resources import load_entry_point
load_entry_point('myCoolApp==0.4.3', 'console_scripts', 'myEntryPoint')()

So it does not import your module, but out-tasks this to load_entry_point. PyInstaller can not detect this when passed the script.

The Recipe

Now instead of running Analysis() on your script, simply run Entrypoint() (see below) on the entry point definition:

a = Entrypoint('myCoolApp', 'console_scripts', 'myEntryPoint')

Entrypoint() automatically creates and analyzes a script which basically does the same as the one shown on the top of this page – but using a syntax PyInstaller can analyze.

For this to work, place the following snippet into your .spec file:

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
    )

Note

This recipe contains possible workaround to #1086 & #305

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.