Как заставить setup.py включить файл `__init__.py` и сделать мой пакет импортируемым?

Я пытаюсь сделать мой проект Python доступным для импорта людьми, которые устанавливают его с помощью pip. Хотя он содержит __init__.py и работает как пакет локально, похоже, я неправильно понимаю, как работает setuptools.

Я запускаю следующие три команды, чтобы загрузить пакет.

python3 setup.py sdist bdist_wheel
python3 -m pip install  --upgrade twine
python3 -m twine upload dist/*

Потом на другой машине запускаю pip3 install smux.py. В результате я могу получить доступ к smux.py как к команде, но при попытке импорта я получаю сообщение об ошибке импорта.

python3
Python 3.8.5 (default, Jul 28 2020, 12:59:40) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import smux
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'smux'

После выполнения приведенных выше команд setup.py я проверил smux.py.egg-info/SOURCES.txt в текущем каталоге и обнаружил, что он содержит следующее:

README.md
setup.py
smux.py
smux.py.egg-info/PKG-INFO
smux.py.egg-info/SOURCES.txt
smux.py.egg-info/dependency_links.txt
smux.py.egg-info/top_level.txt

Файл __init__.py отсутствует.

Как мне добавить этот файл в пакет или изменить вызов установки, чтобы сделать smux импортируемым?

🤔 А знаете ли вы, что...
Python подходит для начинающих программистов благодаря своей простоте и читаемости кода.


1 172
2

Ответы:

Решено

Ваш звонок setup выглядит так:

setup(
  name = "smux.py",
  version='0.1.1',
  scripts=['smux.py'],
  author = "Henry Qin",
  author_email = "[email protected]",
  description = "Simple tmux launcher that will take less than 2 minutes to learn and should work across all versions of tmux",
  long_description=long_description,
  platforms=["All platforms that tmux runs on."],
  license = "MIT",
  url = "https://github.com/hq6/smux"
)

С помощью аргумента scripts вы сообщаете setuptools, что smux.py — это скрипт, а не модуль. Соответственно, он устанавливается как скрипт, а не как модуль.

Ваш код не нуждается в __init__.py и не должен его иметь. Наличие __init__.py не поможет. Вам нужно указать setuptools установить ваш файл как модуль, а не скрипт, и отдельно зарегистрировать точку входа с записью console_scripts в аргументе entry_points:

setup(
    ...
    # no scripts= line
    py_modules=['smux'],
    entry_points = {
        'console_scripts': ['smux=smux:main']
    }
)

Затем, когда ваш код будет установлен, smux можно будет импортировать, и в командной строке будет доступен скрипт smux, который вызывает smux.main.


Другой ответ более кошерный и современный, и я бы использовал его и назвал сценарий smux, если бы у меня не было устаревших сценариев, зависящих от имени smux.py. К сожалению, другие завернули smux.py, и было бы нехорошо сломать их скрипты.

Вот что я сделал, чтобы избежать проблемы циклической зависимости, возникающей из-за того, что имя устаревшего скрипта оканчивается на .py и конфликтует с именем модуля.

Я закончил тем, что изменил вызов setup, чтобы иметь оба следующих.

  py_modules=['smux'],
  scripts=['smux.py']

Мои эксперименты показывают, что это дает желаемый эффект: делает модуль импортируемым при сохранении рабочего скрипта под именем smux.py.