• Pyinstaller打包pikepdf失败的问题排查


    🚀 优质资源分享 🚀

    学习路线指引(点击解锁)知识定位人群定位
    🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
    💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

    问题

    最近在项目里用到了pikepdf这个库,用于实现pdf水印插入的一个小功能,源码调试阶段运行一切OK但是在出包时报了如下异常。

    Traceback (most recent call last):
      File "pikepdf\\_\_init\_\_.py", line 19, in 
     File "PyInstaller\loader\pyimod03\_importers.py", line 495, in exec\_module
     File "pikepdf\\_version.py", line 13, in 
     File "importlib\metadata.py", line 530, in version
     File "importlib\metadata.py", line 503, in distribution
     File "importlib\metadata.py", line 177, in from\_name
    importlib.metadata.PackageNotFoundError: pikepdf
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
     File "main.py", line 1, in 
     File "PyInstaller\loader\pyimod03\_importers.py", line 495, in exec\_module
     File "pikepdf\\_\_init\_\_.py", line 21, in 
    ImportError: Failed to determine version
    [29708] Failed to execute script 'main' due to unhandled exception!
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    异常定位

    打印了两份堆栈的信息,翻翻炸出来点 init.py 的21行,代码如下

    try:
        from ._version import __version__
    except ImportError as _e:  # pragma: no cover
        raise ImportError("Failed to determine version") from _e
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    从.version.py文件导入__version__失败?看看_version.py

    try:
        from importlib_metadata import version as _package_version  # type: ignore
    except ImportError:
        from importlib.metadata import version as _package_version
    
    __version__ = _package_version('pikepdf')
    
    __all__ = ['\_\_version\_\_']
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    再看看上面的异常,也就再_package_version这个函数了。这边可以先写个简单的demo.py验证下,使用pyinstgaller编译后运行。

    # demo.py
    import pikepdf
    
    if __name__ == '\_\_main\_\_':
        print("Hello World")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    # 输出
    Traceback (most recent call last):
      File "pikepdf\\_\_init\_\_.py", line 19, in 
     File "PyInstaller\loader\pyimod03\_importers.py", line 495, in exec\_module
     File "pikepdf\\_version.py", line 13, in 
     File "importlib\metadata.py", line 530, in version
     File "importlib\metadata.py", line 503, in distribution
     File "importlib\metadata.py", line 177, in from\_name
    importlib.metadata.PackageNotFoundError: pikepdf
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
     File "main.py", line 1, in 
     File "PyInstaller\loader\pyimod03\_importers.py", line 495, in exec\_module
     File "pikepdf\\_\_init\_\_.py", line 21, in 
    ImportError: Failed to determine version
    [29708] Failed to execute script 'main' due to unhandled exception!
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    符合预期。所以说importlib.metadata.version 无法在pyinstaller打包后运行?

    问题原因

    对于pkgs_to_check_at_runtime中列出的每个包,需要通过在spec文件中使用copy_metadata(name)来收集元数据。说白了就是pyinstaller打包后缺少对应的metadata信息。

    修复方案

    1. 降低版本pikepdf的版本

    这个库以前用过,没有出这个幺蛾子。看了下旧版本的源代码,一下是5.1.3版本之前的get_version实现,没有使用importlib库,自然也不会有问题。

    from pkg_resources import DistributionNotFound
    from pkg_resources import get_distribution as _get_distribution
    
    try:
        __version__ = _get_distribution(__package__).version
    except DistributionNotFound:  # pragma: no cover
        __version__ = "Not installed"
    
    __all__ = ['\_\_version\_\_']
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2. 在pyinstaller打包时指定copy-metadata

    像这样

    pyinstaller -F --copy-metadata pikepdf main.py
    
    
    • 1
    • 2

    碎碎念

    我觉得完全可以像版本5.1.3之前一样,获取不到时赋值为"Not installed"一样就行。然后提了个pr[https://github.com/pikepdf/pikepdf/pull/358]给作者,可惜作者认为这是importlib的锅(不改),倒也时说得过去。

  • 相关阅读:
    技术分享 | App测试时常用的adb命令你都掌握了哪些呢?
    基于SpringBoot的在线题库管理系统的设计与实现(源码+lw+部署文档+讲解等)
    桶装水订水小程序app,线上预约订水更便捷
    SpringBoot简单整理
    leetcode 907. 子数组的最小值之和(重做)
    <Python>PyQt5,多窗口之间参数传递和函数调用
    理论和实践详解RabbitMQ惰性/延迟队列(lazy queues)(带测试样例及分析)
    Termux 使用
    HTML、css、javascript的区别
    gcc: -fno-threadsafe-statics: __cxa_guard_acquire;__cxa_guard_release
  • 原文地址:https://blog.csdn.net/u013190417/article/details/125512748