Я опубликовал пакет PyPI, для которого требуются некоторые дополнительные пакеты Apt, поэтому в моем репозитории Github есть setup.sh
, который я хочу выполнить вместе с процессом установки pip install mypackage
(т. е. всякий раз, когда пользователь запускает pip install mypackage
, он должен запускать сценарий оболочки во время процесс установки.)
Я сделал setup.py
, чтобы использовать параметр cmdclass вот так
from setuptools import setup, find_packages
from setuptools.command.install import install
import subprocess
import codecs
import os
class CustomInstall(install):
def run(self):
subprocess.check_call("bash setup.sh", shell=True)
install.run(self)
with open('requirements.txt') as f:
requirements = f.read().splitlines()
setup(
...
cmdclass = {'install': CustomInstall}, install_requires=requirements,
...
)
Когда я обычно собираю его с помощью python setup.py sdist bdist_wheel
, класс Custominstall
работает нормально, но во время установки pip он игнорирует класс Custominstall
.
Наконец я нашел эту ветку stackoverflow, из которой я узнал, что мне нужно только запустить python setup.py sdist
, что я и сделал, и получил файл tar.gz в папке dist
, но когда я перехожу к установке этого файла tar.gz с помощью pip install, я получаю
with open('requirements.txt') as f:
^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'requirements.txt'
Что я делаю не так? Путь неверный? Файл требований находится в родительской папке моего репозитория (а не внутри какой-либо другой папки).
Непосредственная проблема заключается в том, что процесс pip
запускается в любом каталоге, в котором находится вызывающий пользователь. Возможно, см. Также Что такое текущий рабочий каталог?
Решение этой проблемы: Как мне получить путь и имя файла Python, который выполняется в данный момент? -- по сути, проверьте __file__
и извлеките из него каталог, к которому вы хотите получить доступ.
Однако предпосылка вашего вопроса кажется глубоко ошибочной. Вам следует объявить зависимости от тех пакетов Python, от которых вы зависите, и позволить pip
взять их оттуда; или же создайте пакет Debian, а затем объявите все свои зависимости в терминах других пакетов Debian. (Конечно, вы можете сделать и то, и другое. Конечно, в последнем случае вам, вероятно, следует сделать и первое, чтобы ваш пакет можно было установить на платформах, отличных от Debian.)
Вы не сообщили нам, от каких пакетов будет зависеть ваш, поэтому мы можем сформулировать это только в самых общих чертах. Допустим, ваша посылка зависит от requests
и beautifulsoup4
. Тогда ваш setup.py
должен объявить эти зависимости:
setup(
...
install_requires=["requests", "beautifulsoup4"],
...
)
Для удобства вы также можете объявить их в requirements.txt
, но этот файл не играет формальной роли в упаковке Python. Ваши зависимости должны быть объявлены в setup.py
(или для современных пакетов определенно предпочтительнее pyproject.toml
или setup.cfg
).
Если некоторые из этих зависимостей можно выполнить через менеджер пакетов ОС, предоставление другого механизма установки для удобства не является обязательным, но является приятным дополнением. Ваш пакетный скрипт не должен от этого зависеть (а возможно и наоборот).
#!/bin/sh
apt-get install -y python3-requests python3-bs4
(Возможно, обратите внимание также на то, что пакеты Debian обычно имеют другие имена, чем соответствующие пакеты Python. Это неприятно, но в принципе неизбежно.)
Я использовал небольшой обходной путь, чтобы распечатать файлы из каталогов с помощью script_dir = os.path.dirname(os.path.realpath(__file__)) dir_contents = os.listdir(script_dir) print("Contents of the directory:") for item in dir_contents: print(item)
, и увидел, что требований.py и setup.py там нет. Содержимое каталога было: LICENSE PKG-INFO README.md setup.cfg setup.py mypackage mypackage.egg-info build
Хорошо, я нашел частичное решение, основанное на том, что вы сказали, я помещу требования pip в install_requires = [] и что я сделал, это переместил сценарий setup.sh в mypackage/setup.sh и использовал параметр package_data в setup() package_data = {'mypackage': ['setup.sh']}, include_package_data=True,
и изменил мой класс CustomIntall как class CustomInstall(install): def run(self): script_dir = os.path.dirname(os.path.realpath(__file__)) setup_sh= script_dir+"/mypackage/setup.sh" os.system(f"bash {setup_sh}") install.run(self)
Странно, что он использует apt вместо pkg, тогда как мой скрипт везде использовал pkg./data/data/com.termux/files/usr/tmp/pip-req-build-ky2zym9d/setup.py No mirror or mirror group selected. You might want to select one by running 'termux-change-repo' Checking availability of current mirror: [*] https://termux.astra.in.ua/apt/termux-main: ok WARNING: apt does not have a stable CLI interface. Use with caution in scripts. Hit:1 https://termux.astra.in.ua/apt/termux-main stable InRelease Reading package lists... ...... ...... ......
Причина, по которой это происходит, заключается в том, что ваш setup.py
зависит от requirements.txt
, но вы не упаковываете requirements.txt
в свой дистрибутив. Если вы откроете .tar.gz
, вы не найдете свой requirements.txt
файл. Чтобы это исправить, создайте файл MANIFEST.in
с содержимым:
include requirements.txt
И добавьте include_package_data=True
к своему setup
звонку.
Вам нужно будет добавить любые другие файлы, не относящиеся к Python, которые вы хотите включить в свой дистрибутив .tar.gz
, например, ваш скрипт setup.sh
.
Когда вы создадите свой исходный дистрибутив с этими изменениями, вы увидите, что requirements.txt
теперь включен в содержимое вашего дистрибутива сборки.
Альтернативно, вы также можете использовать для этого ключевое слово package_data
в setup
(или [options.package_data]
в setup.cfg
). См.: эту ссылку для более подробного объяснения.
Однако, вообще говоря, запускать сценарии оболочки или устанавливать системные пакеты как часть вашего setup.py
. Это усложнит использование вашего пакета, а не упростит его.
Если ваш пакет зависит от зависимостей Python, используйте вместо него install_requires
.
Я боролся несколько дней, хотя решение было таким простым 😦. Большое спасибо. Теперь pip install работает нормально и может найти все необходимые файлы. Я понимаю ваше предложение, но у меня нет возможности полагаться на эти системные пакеты, поскольку мой скрипт Python полностью полагается на эти пакеты, такие как диалог для gui, ffmpeg, termux-api и т. д. Вы можете посмотреть мой репозиторий
Обычно вы оставляете пользователю право убедиться, что у него установлены правильные системные пакеты. Вы можете выдать полезную ошибку, если ваши внешние зависимости не найдены, например if not shutil.which('ffmpeg'): raise EnvironmentError("ffmpeg is not installed, please install it e.g., with 'apt install ffmpeg'")
или что-то в этом роде. Множество популярных библиотек Python зависят от системных пакетов — я гарантирую, что ни одна из них не установит эти зависимости за вас! В качестве альтернативы вы можете рассмотреть возможность создания пакета conda или собственного пакета Debian для установки всего @AyusChatterjee
«Файл требований находится в родительской папке моего репозитория (а не внутри какой-либо другой папки)» — но что вы видите в архиве?