2025, Oct 06 01:16
Как собрать spire.xls с PyInstaller: правильное добавление DLL
Почему сборка с PyInstaller ломает spire.xls и даёт AttributeError при загрузке DLL. Решение: добавляйте .dll как бинарные через флаг --add-binary в сборке.
Упаковка инструмента Python, который использует spire.xls, может казаться обманчиво простой: из IDE он работает безупречно, но исполняемый файл, собранный с PyInstaller, падает во время выполнения. Признак — AttributeError, который проявляется только после «заморозки». Хорошая новость: дело не в логике вашего кода; это проблема упаковки, связанная с тем, как PyInstaller обращается с разными типами ресурсов.
Симптом
Из исходников всё работает, а упакованный .exe — нет. Программа завершается с сообщением вроде:
An exception of type AttributeError occurred. Arguments: ('NoneType' object has no attribute 'Workbook_CreateWorkbook',)
Перед сбоем сборка запускалась командой, которая пытается упаковать файлы .dll как данные. Например:
pyinstaller --noconfirm --onefile --console 
  --add-data "D:\work\apps\venv\Lib\site-packages\spire\xls\lib\Spire.Xls.Base.dll;." 
  --add-data "D:\work\apps\venv\Lib\site-packages\spire\xls\bin\Spire.XLS.dll;." 
  --add-data "D:\work\apps\venv\Lib\site-packages\spire\xls\lib\libSkiaSharp.dll;." 
  "D:\work\apps\RunReports.py"
Что происходит
PyInstaller по-разному обрабатывает ресурсы-данные и нативные библиотеки. Использованный выше флаг предназначен для неисполняемых ресурсов. Чтобы включить файлы .dll, их необходимо добавлять как бинарные. Когда .dll упакованы некорректно, код во время выполнения, который на них опирается, не загружается как ожидается, и возникают ошибки при создании объектов или вызове методов.
Решение
Перейдите от включения .dll как данных к включению их как бинарных файлов. В этом и состоит весь смысл исправления.
pyinstaller 
  --add-binary ./libSkiaSharp.dll:./spire/pdf/lib 
  --add-binary ./Spire.Pdf.Base.dll:./spire/pdf/lib 
  invoice_build.py
Обновите имена и пути .dll под вашу среду. Важно: используйте --add-binary для каждой .dll и указывайте назначение внутри упакованного приложения.
Почему это важно
Такие библиотеки, как spire.xls, зависят от нативных компонентов. Если при упаковке они не включены как бинарные, исполняемый файл не будет иметь к ним доступа во время работы. Правильный флаг PyInstaller согласует сборку с тем, как на самом деле загружаются нативные библиотеки, и предотвращает трудноотлавливаемые сбои, которые проявляются только после заморозки.
Вывод
Если ваш скрипт на spire.xls работает из интерпретатора, но ломается после упаковки, сперва посмотрите, как вы включаете .dll. Не передавайте их как данные. Используйте --add-binary для каждой нужной .dll, проверьте пути и пересоберите. Это одно изменение снимает расхождение между поведением при разработке и после упаковки и делает распространение предсказуемым.
Статья основана на вопросе со StackOverflow от goryef и ответе от Dheeraj Malik.