【问题标题】:Custom package with dependency init error: ModuleNotFoundError or ImportError具有依赖项初始化错误的自定义包:ModuleNotFoundError 或 ImportError
【发布时间】:2021-02-19 11:40:32
【问题描述】:

我正在创建一个具有以下结构的自定义包:

test_package
    │   README.md
    │   setup.py
    │
    ├───my_package
    │       my_package.py
    │       __init__.py
    │
    └───tests
            tests.py

我的包依赖于pygdbmi,所以我将它添加到所需依赖项列表中。

我正在使用__init__.py 导入模块

__init__.py:

from .my_package import my_class

__version__ = '0.0.1'
__title__ = 'my_package'

我的课是:

my_package.py:

from pygdbmi.gdbcontroller import GdbController

class my_class:
    def __init__(self):
        print("my_class!!")
        self.gdbmi = GdbController()

问题是当我运行python setup.py install clean 时,我得到一个ModuleNotFoundError

python setup.py install clean
Traceback (most recent call last):
  File "setup.py", line 2, in <module>
    import my_package
  File "c:\test_package\my_package\__init__.py", line 1, in <module>
    from .my_package import my_class
  File "c:\test_package\my_package\my_package.py", line 2, in <module>
    from pygdbmi.gdbcontroller import GdbController
ModuleNotFoundError: No module named 'pygdbmi

这很明显,因为__init__ 正在导入my_package,它会因为我还没有安装pygdbmi 而中断。

我试图通过从__init__.py 删除导入来解决此问题:

__version__ = '0.0.1'
__title__ = 'my_package'

它安装正确,但现在我无法导入我的包。当我尝试运行一些测试时:

python tests.py
Traceback (most recent call last):
  File "tests.py", line 3, in <module>
    from my_package import my_class
ImportError: cannot import name 'my_class' from 'my_package' (C:\Users\lalalala\AppData\Local\Continuum\anaconda3-32\lib\site-packages\my_package-0.0.1-py3.7.egg\my_package\__init__.py)

我该如何解决这个问题?我想保留定义版本等的 __init__ 结构,因为这似乎是 Pythonic 的方式。

非常感谢!!!

setup.py:

import setuptools
import my_package

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    # Project information
    name=my_package.__title__,
    version=my_package.__version__,
    long_description_content_type="text/markdown",
    packages=setuptools.find_packages(),
    install_requires=["pygdbmi"],
    python_requires='>=3.7',
    # Tests
    test_suite='tests'
)

my_package.py:

from pygdbmi.gdbcontroller import GdbController

class my_class:
    def __init__(self):
        print("my_class!!")
        self.gdbmi = GdbController()

tests.py:

import unittest

from my_package import my_class

class some_test(unittest.TestCase):
    def test_constructor(self):
        self.assertIsNotNone(my_class())

if __name__ == '__main__':
    unittest.main()

【问题讨论】:

    标签: python python-3.x setuptools python-packaging


    【解决方案1】:

    这听起来令人失望,但我几乎会放弃并做以下事情:

    • install_requires 中删除pygdbmi
    • pygdbmi 放入requirements.txt
    • from pygdbmi.gdbcontroller import GdbController 包装在try...except 块中,这将打印一条有用的消息,告诉用户如果他们希望使用该软件包,他们需要手动安装pygdbmi;然后重新引发异常
    • 在任何安装说明或部署脚本中,只需在python setup.py install 之前添加pip install -r requirements.txt

    据我所知,这并不是一种非常常见的做法——我遇到了几个来自 PyPI 的包,它们需要我手动安装它们的先决条件。

    【讨论】:

    • 感谢您的回答,但我可以在导入包时使用from my_package.my_package import my_class,我仍然可以让 Python 自动安装依赖项。据我所知,在__init__.py 中使用相对导入并不常见。
    • 这可能并不少见,但这是不好的做法。一个 requirements.txt 永远不应该是分发的一部分。
    【解决方案2】:

    我该如何解决这个问题?我想保留定义版本等的__init__ 结构,因为这似乎是 Pythonic 的方式。

    不是,名称和版本是包元数据,不属于源代码。它属于您的包定义,在您的情况下意味着setup.py

    你的__init__.py 应该反过来工作,并通过与 python 环境交互和它自己的安装来获取它的信息:

    from importlib import metadata
    
    # this works, but usually people just write the name as a string here.
    # not 100% DRY, but it's not like the package name could ever change
    __title__ = __name__
    # if you're stuck on python 3.7 or older, importlib-metadata is a 
    # third-party package that can be used as a drop-in instead
    __version__ = metadata.version(__title__)
    

    最重要的信息应该是永远不要将代码导入构建脚本。它可以 1)创建令人讨厌的鸡蛋问题,除非已经安装了旧版本,否则无法构建您的代码,以及 2)将所有运行时依赖项转换为构建时依赖项,这就是您所要做的体验。

    您也许可以解决这两个问题,但更简单的方法是按照上述方式设置您的包裹数据,并且从一开始就不会遇到问题。

    【讨论】:

    • 非常感谢!这是一个非常优雅的解决方案。 __init__.py中通常包含哪些元数据?
    • 我个人只有版本,因为这是我最终在我的代码中实际使用的唯一版本。
    猜你喜欢
    • 1970-01-01
    • 2019-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-16
    • 1970-01-01
    • 2014-01-15
    • 1970-01-01
    相关资源
    最近更新 更多