Python: почему pip install не развертывает мой код из sdist?

Я пытаюсь упаковать небольшое приложение Python 3.9 в файл дистрибутива программного обеспечения .tar.gz (также известный как «sdist»).

Мой код имеет такую ​​структуру:

my-code
  PackageA
    PackageB
      Module1.py
      __init__.py 
    __init__.py
  setup.py

Мой файл setup.py выглядит так:

from setuptools import find_packages, setup

setup(
    name = "my-code",
    version = "0.1",
    install_requires=[],
    packages=find_packages(include=["PackageA.PackageB"]),
    include_package_data=True,
    description = "My Code"
)


Когда я запускаю python setup.py sdist --formats=gztar, я успешно получаю my-code-0.1.tar.gz файл и вижу, что он содержит мои .py файлы.

Однако, когда я запускаю pip install my-code-0.1.tar.gz, кажется, что pip не компилирует и не развертывает мои .py файлы. т.е. когда я устанавливаю в venv и просматриваю каталог Lib/site-packages для venv, я вижу только каталог с именем my_code-0.1-py3.9.egg-info, а моего кода в нем нет. Попытки импортировать или запустить мой модуль, который находится в PackageA.PackageB, не увенчались успехом.

Мой вопрос - Почему мой код не собирается и не устанавливается pip install и как это исправить?

🤔 А знаете ли вы, что...
Python является интерпретируемым языком программирования.


2
49
1

Ответ:

Решено

TL;DR, потому что find_packages(include=["PackageA.PackageB"]) отфильтрует родительский PackageA, поэтому он не включен в установку. Просто используйте

setup(
    packages=find_packages(),
    ...
)

и все будет хорошо.

Более длинное объяснение состоит в том, что аргумент include не означает «включать в дополнение к тому, что find_packages() находит». Это означает «включать только пакеты, найденные find_packages(), которые находятся в списке фильтров include», поэтому он может только уменьшить выбор пакета. Сравните вывод

$ python -c "from setuptools import find_packages as f; print(f())"
['PackageA', 'PackageA.PackageB']

против

$ python -c "from setuptools import find_packages as f; print(f(include=['PackageA.PackageB']))"
['PackageA.PackageB']

Поскольку PackageA не включен, PackageA/__init__.py будет опущен в исходном дистрибутиве, что фактически удалит свойство пакета из PackageA - в tar-архиве теперь это будет обычный каталог. Бег pip install mydist.tar.gz больше не найдет PackageA, поэтому PackageA.PackageB тоже нельзя найти. Следовательно, ничего не будет установлено. Раздел Автоматическое обнаружение пакетов в документах setuptools содержит краткое упоминание аргументов include и exclude для find_packages(), но IMO гораздо более полезна строка документации функции:

>>> from setuptools import find_packages
>>> help(find_packages)
find(where='.', exclude=(), include=('*',)) method of builtins.type instance
    Return a list all Python packages found within directory 'where'

    ...

    'include' is a sequence of package names to include.  If it's
    specified, only the named packages will be included.  If it's not
    specified, all found packages will be included.  'include' can contain
    shell style wildcard patterns just like 'exclude'.