代码之家  ›  专栏  ›  技术社区  ›  Leonardo

自定义包出现依赖项初始化错误:ModuleNotFoundError或ImportError

  •  0
  • Leonardo  · 技术社区  · 4 年前

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

    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__ 定义版本的结构等,因为这似乎是Python的方式。

    非常感谢!!!

    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 :

    从pygdbmi.gdbcontroller导入gdbcontroller
    
    类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()
    
    0 回复  |  直到 4 年前
        1
  •  2
  •   Arne    4 年前

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

    它不是,名称和版本是包元数据,不属于源代码。它属于你的包定义,意思是 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)将所有运行时依赖关系转化为构建时依赖关系,这就是你正在经历的。

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