具有依赖项初始化错误的自定义包:ModuleNotFoundError 或 ImportError
我正在创建一个具有以下结构的自定义包:
I'm creating a custom package that has the following structure:
test_package
│ README.md
│ setup.py
│
├───my_package
│ my_package.py
│ __init__.py
│
└───tests
tests.py
我的包依赖于pygdbmi,所以我把它添加到了所需的依赖列表中.
My package depends on pygdbmi, so I added it to the list of required dependencies.
我正在使用 __init__.py
导入模块
I'm using __init__.py
to import the modules
__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
:
The problem is that when I run python setup.py install clean
, I get a 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.
Which is obvious, because __init__
is importing my_package
, it breaks because I don't have pygdbmi installed yet.
我试图通过从 __init__.py
中删除导入来解决这个问题:
I tried to fix this by removing the import from __init__.py
:
__version__ = '0.0.1'
__title__ = 'my_package'
它安装正确,但现在我无法导入我的包.当我尝试运行一些测试时:
It installs correctly, but now I can't import my package. when I try to run some tests:
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 的方式.
How do I fix this? I'd like to keep the __init__
structure of defining the version, etc, as it seems the Pythonic way to do it.
非常感谢!!!
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()
我该如何解决这个问题?我想保留定义版本等的
__init__
结构,因为这似乎是 Pythonic 的方式.
How do I fix this? I'd like to keep the
__init__
structure of defining the version, etc, as it seems the Pythonic way to do it.
不是,名称和版本是包元数据,不属于源代码.它属于您的包定义,在您的情况下意味着 setup.py
.
It is not, name and version are package metadata, which does not belong into the source code. It belongs into your package definition, meaning setup.py
in your case.
您的 __init__.py
应该反过来工作,并从与 python 环境及其自己的安装交互中获取信息:
Your __init__.py
should work the other way round, and get its info from interacting with the python environment and its own installation:
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) 将所有运行时依赖项转换为构建时依赖项,这就是您的情况体验.
The most important take-away message should be that you should never import your code into your build-script. It can 1) create nasty chicken-egg problems where your code can't be built unless an older version of it was installed already, and 2) turn all your run-time dependencies into built-time dependencies, which is what you're experiencing.
您或许可以同时解决这两个问题,但更简单的方法是按照上述方式设置您的包数据,并且永远不会遇到问题.
You may be able to work around both of these problem, but the easier way would be to set up your package data in the way described above, and never get the issue in the first place.