#python #python-3.x #setuptools #python-packaging
#python #python-3.x #setuptools #python-упаковка
Вопрос:
Я создаю пользовательский пакет, который имеет следующую структуру:
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_packagemy_package__init__.py", line 1, in <module>
from .my_package import my_class
File "c:test_packagemy_packagemy_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:UserslalalalaAppDataLocalContinuumanaconda3-32libsite-packagesmy_package-0.0.1-py3.7.eggmy_package__init__.py)
Как мне это исправить? Я бы хотел сохранить __init__
структуру определения версии и т. Д., Поскольку это кажется питоническим способом сделать это.
Большое спасибо!!!
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()
Ответ №1:
Как мне это исправить? Я бы хотел сохранить
__init__
структуру определения версии и т. Д., Поскольку это кажется питоническим способом сделать это.
Это не так, имя и версия являются метаданными пакета, которые не входят в исходный код. Это относится к определению вашего пакета, то 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__)
Наиболее важным выводом должно быть то, что вы никогда не должны импортировать свой код в свой build-script. Это может 1) создать неприятные проблемы с куриным яйцом, когда ваш код не может быть собран, если его более старая версия уже не была установлена, и 2) превратить все ваши зависимости во время выполнения во встроенные зависимости, с чем вы и сталкиваетесь.
Возможно, вы сможете обойти обе эти проблемы, но более простым способом было бы настроить данные вашего пакета описанным выше способом и никогда не столкнуться с проблемой в первую очередь.
Комментарии:
1. Большое спасибо! Это очень элегантное решение. Какие метаданные обычно включаются в
__init__.py
?2. У меня лично есть только версия, потому что это единственная, которую я в конечном итоге использую в своем коде.