sanic-org / html5tagger

Create HTML documents from Python
The Unlicense
29 stars 4 forks source link

Version issues with PyInstaller #8

Closed dmckeone closed 1 year ago

dmckeone commented 1 year ago

I'm experimenting with using PyInstaller with Sanic, and I'm getting this strange error.

pkg_resources.DistributionNotFound: The 'html5tagger>=1.2.1' distribution was not found and is required by tracerite
[22687] Failed to execute script 'main' due to unhandled exception!

It's strange because I've installed html5tagger==1.3.0

% pip freeze | grep html5tagger
html5tagger==1.3.0

It's also strange because html5tagger is being picked up by pyinstaller

1123 INFO: Packages required by sanic:
['sanic_routing', 'httptools', 'aiofiles', 'websockets', 'multidict', 'html5tagger', 'tracerite', 'uvloop']
1224 INFO: Packages required by tracerite:
['html5tagger']

I noticed that the __version__ appears to be hard coded in this project (despite the PyPI version being 1.3.0):

__version__ = "1.2.0"

Ref: https://github.com/sanic-org/html5tagger/blob/main/html5tagger/__init__.py#L4

and that differs from something like, tracerite, where it's using pkg_resources:

import pkg_resources

__version__ = pkg_resources.require(__name__)[0].version

Ref: https://github.com/sanic-org/tracerite/blob/main/tracerite/__init__.py#L8

I don't know enough about packaging to say that for sure, but perhaps that's an issue in this situation?

dmckeone commented 1 year ago

I did ultimately figure this out with PyInstaller by including a collect_all call for html5tagger (amongst other Sanic packages)

The final Pyinstaller spec file for my sample was this:

(the project has a fairly standard setup of sanic sqlalchemy / alembic and click)

# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_all

name = 'sanic_playground'

datas = [
    ('log_config.json', '.'),
    ('alembic.ini', '.'),
    ('migrations', 'migrations')
]

binaries = []
hiddenimports = [
    'asyncpg'
]

# Ensure that all sanic dependencies are collected
for pkg_name in ['asyncpg', 'html5tagger', 'sanic', 'sanic_ext', 'sqlalchemy', 'tracerite']:
    pkg_collect = collect_all(pkg_name)
    datas += pkg_collect[0]
    binaries += pkg_collect[1]
    hiddenimports += pkg_collect[2]

# -------------------------------
# DEFAULT GENERATED CONTENT BELOW
# -------------------------------

block_cipher = None

a = Analysis(
    ['main.py'],
    pathex=[],
    binaries=binaries,
    datas=datas,
    hiddenimports=hiddenimports,
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name=name,
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)
Tronic commented 1 year ago

Good to know. Let us know if you find out it needs any changes to Sanic packages to make it more fluent.