Сборка пакета python для скрипта CLI

#python-3.x #build #package #pypi #modulenotfounderror

#python-3.x #сборка #пакет #pypi #ошибка modulenotfounderror

Вопрос:

Я пытаюсь создать свой первый пакет python и опубликовать его в PyPI. Моя цель — предоставить определенную функцию через CLI, чтобы конечный пользователь мог установить ее через pip, например pip3 install my-package , и использовать ее непосредственно из терминала my-package -fa first_argument -sa second_argument .

Допустим, вызывается мой пакет fp-test-package . Я сделал макет структуры папок моего проекта, который вы можете найти здесь https://github.com/fabiopipitone/fp-test-package .

В макете я также вставил в качестве требований один из пакетов, необходимых в реальном проекте (tqdm), и сохранил точно такое же соглашение (дефис во внешней папке и репозитории github, подчеркивания для внутренних пакетов, одинаковая позиция и импорт для помощников и утилит и так далее). Таким образом, если он работает на макете, он должен работать и на реальном проекте.

Вот структура каталога проекта

 fp-test-package
├── fp_test_package
│   ├── __init__.py
│   ├── fp_test_package.py
│   ├── helpers
│   │   ├── arguments_checkers.py
│   │   ├── csv_handlers.py
│   │   └── utility_functions.py
│   └── utils
│       ├── __init__.py
│       ├── CustomLogger.py
│       └── TqdmLoggingHandler.py
├── LICENSE
├── README.rst
├── requirements.txt
├── setup.py
├── docs
└── tests
  

Вот что такое setup.py

 from setuptools import setup, find_packages
with open("README.rst", "r") as fh:
  long_description = fh.read()

setup( 
  name ='fp-test-package', 
  version ='0.0.1', 
  description='Simple test building a CLI tool package',
  long_description=long_description,
  long_description_content_type='text/x-rst', 
  license ='GPLv2', 
  packages = find_packages(), 
  entry_points ={ 
    'console_scripts': [ 
      'fp_test_package = fp_test_package.py:main'
    ] 
  }, 
  classifiers =( 
    "Programming Language :: Python :: 3", 
    "License :: OSI Approved :: GNU General Public License v2", 
    "Operating System :: Linux", 
  ), 
  keywords ='test packaging fabiopipitone', 
  install_requires = ['tqdm>=4.49.0'], 
  zip_safe = False
) 
  

Я попробовал следовать нескольким инструкциям о том, как создавать и публиковать пакет CLI python, затем я попробовал sudo python3 setup.py install из fp-test-package каталога (тот же уровень setup.py ). Кажется, он устанавливает пакет (я могу найти запись fp-test-package==0.0.1 в pip3 freeze ), но если я попробую fp-test-package в терминале, он вернется fp-test-package: command not found , если я попробую fp_test_package , он вернется:

 Traceback (most recent call last):
  File "/usr/local/bin/fp_test_package", line 11, in <module>
    load_entry_point('fp-test-package==0.0.1', 'console_scripts', 'fp_test_package')()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 490, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2854, in load_entry_point
    return ep.load()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2445, in load
    return self.resolve()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2451, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
ModuleNotFoundError: No module named 'fp_test_package.py'
  

Затем я пошел с sudo pip3 uninstall fp_test_package , который успешно удалил пакет. Я удалил ранее созданные build dist fp_test_package.egg-info каталоги и и попробовал pip3 install -e . (снова изнутри fp-test-package ). Он создал fp_test_package.egg-info каталог, но опять же, запустив fp_test_package консоль, он возвращает

 Traceback (most recent call last):
  File "/usr/local/bin/fp_test_package", line 11, in <module>
    load_entry_point('fp-test-package==0.0.1', 'console_scripts', 'fp_test_package')()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 490, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2854, in load_entry_point
    return ep.load()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2445, in load
    return self.resolve()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2451, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
ModuleNotFoundError: No module named 'fp_test_package.py'
  

Итак, чего мне не хватает? Я хотел бы просто упаковать и установить его, чтобы при вводе пользователем fp-test-package or fp_test_package он возвращал сообщение об ошибке о требуемом --export_path аргументе (означает, что скрипт запущен правильно), например, когда я вызываю его с консоли:

 $ python3 fp_test_package/fp_test_package.py
                                
usage: fp_test_package.py [-h] -ep EXPORT_PATH [-sa SECOND_ARGUMENT] [-ta THIRD_ARGUMENT]
fp_test_package.py: error: the following arguments are required: -ep/--export_path
  

Как я могу это сделать?

РЕДАКТИРОВАТЬ: я заметил, что @Dustin затем указал на setup.py . На самом деле, мой коллега создал фиктивный репозиторий, изменив console_scripts часть. Теперь на моей машине коллеги все работает так, как ожидалось. На моем, после извлечения репозитория и повторного запуска python3 setup.py install , при вызове fp_test_package из консоли он возвращает следующее:

 Traceback (most recent call last):
  File "/usr/local/bin/fp_test_package", line 11, in <module>
    load_entry_point('fp-test-package==0.0.1', 'console_scripts', 'fp_test_package')()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 490, in load_entry_point
    return get_distribution(dist).load_entry_point(group, name)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2854, in load_entry_point
    return ep.load()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2445, in load
    return self.resolve()
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 2451, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "/usr/local/bin/fp_test_package.py", line 4, in <module>
    __import__('pkg_resources').run_script('fp-test-package==0.0.1', 'fp_test_package.py')
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 667, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "/usr/lib/python3/dist-packages/pkg_resources/__init__.py", line 1452, in run_script
    raise ResolutionError(
pkg_resources.ResolutionError: Script 'scripts/fp_test_package.py' not found in metadata at '/usr/local/lib/python3.8/dist-packages/fp_test_package-0.0.1-py3.8.egg/EGG-INFO'
  

Есть идеи?

Ответ №1:

У вас setup.py есть:

   entry_points ={ 
    'console_scripts': [ 
      'fp_test_package = fp_test_package.py:main'
    ] 
  }, 
  

Это эквивалентно следующему импорту:

 from fp_test_package.py import main as fp_test_package
  

Проблема в том, что у вас нет fp_test_package.py модуля, у вас есть fp_test_package модуль (каталог с __init__.py файлом), который содержит fp_test_package подмодуль (имена модулей не включают расширение).

Предполагая, что ваш fp_test_package.py файл определяет main функцию, вы можете изменить это на:

   entry_points ={ 
    'console_scripts': [ 
      'fp_test_package = fp_test_package.fp_test_package:main'
    ] 
  }, 
  

Или вы могли бы переместить свою main функцию в fp_test_package/__init__.py , чтобы она могла быть:

   entry_points ={ 
    'console_scripts': [ 
      'fp_test_package = fp_test_package:main'
    ] 
  }, 
  

Комментарии:

1. Я заметил, на что вы указали, и мой коллега выбрал репозиторий git. После слияния вы можете заметить изменения в setup.py том же решении, которое вы опубликовали. Что забавно, так это то, что теперь все работает на моей машине коллеги, но после того, как я вытащил новую версию и повторно запустил установку с python3 setup.py install помощью , при попытке использовать пакет fp_test_package с помощью консоли он возвращает: « … pkg_resources. Ошибка разрешения: сценарий ‘scripts/fp_test_package.py ‘ не найдено в метаданных по адресу ‘/usr/local/lib/python3.8/dist-packages/fp_test_package-0.0.1-py3.8.egg/EGG-INFO’ «

2. Вам необходимо полностью удалить пакет и переустановить его, желательно с pip install . помощью instead .

3. Что вы подразумеваете под его полной отменой? Я пошел с sudo pip3 uninstall fp_test_package , а затем sudo rm -rf fp_test_package.egg-info dist build удалил созданные каталоги. Затем я попробовал оба sudo python3 setup.py install и pip3 install -e . (сначала один, затем удалите все и попробуйте другой), и оба возвращают одну и ту же ошибку. Я даже пробовал с venv. Та же ошибка. После установки, запуска pip3 list , только строка моего пакета ( fp-test-package 0.0.1 ) имеет Location значение столбца (на компьютере моего коллеги оно пустое). Это что-нибудь значит?

4. Все /usr/local/lib/python3.8/dist-packages/fp_test_package-0.0.1-py3.8.egg еще существует после его удаления?

5. Это не так. После pip3 uninstall fp_test_package и все rm -rf , если я запускаюсь find . -name fp_test* как пользователь sudo из / , я получаю только ./home/fabio/Desktop/test_python_package/fp-test-package/fp_test_package и ./home/fabio/Desktop/test_python_package/fp-test-package/fp_test_package/fp_test_package.py , тогда единственными ссылками на fp_test* являются ссылки на fp-test-package папку. Теперь, если я запущу pip3 install -e . столбец местоположения fp_test_package строки в pip3 list шоу /home/fabio/Desktop/test_python_package/fp-test-package .

Ответ №2:

Хорошо, в конце концов я нашел проблему в моем конкретном случае. Что вызвало эту загадочную ошибку при установке с python3 setup.py install

pkg_resources.ResolutionError: Script 'scripts/fp_test_package.py' not found in metadata at '/usr/local/lib/python3.8/dist-packages/fp_test_package-0.0.1-py3.8.egg/EGG-INFO'

или эквивалентная ошибка при установке с pip3 install .

pkg_resources.ResolutionError: Script 'scripts/fp_test_package.py' not found in metadata at '/home/fabio/Desktop/test_python_package/fp-test-package/fp_test_package-0.0.1-py3.8.egg/EGG-INFO'

было отсутствие в переменной $PATH требуемого пути для правильного запуска скрипта.

Подводя итог, то, что я сделал, было:

  • редактирование setup.py файла в соответствии с предложениями Дастина о console_scripts
  • сборка и установка запущенного пакета pip3 install . (или pip3 install -e . , если вы хотите, редактируемой версии) из fp-test-package папки.
  • проверка того, с помощью чего был создан двоичный файл скрипта which fp_test_package (в моем случае /home/fabio/.local/bin/fp_test_package )
  • добавив это path в переменную $PATH env (в моем .bashrc — и .zshrc с тех пор, как я использую zsh — я добавил строку export PATH="$HOME/.local/bin:$PATH" )

Теперь все работает так, как ожидалось. Я не буду удалять правильную версию репозитория, поэтому, если кто-то хочет использовать рабочий скелет для запуска проекта, его можно найти там.