Python setuptools install_requires игнорируется при переопределении cmdclass
У меня есть setup.py
, который выглядит так:
from setuptools import setup
from subprocess import call
from setuptools.command.install import install
class MyInstall(install):
def run(self):
call(["pip install -r requirements.txt --no-clean"], shell=True)
install.run(self)
setup(
author='Attila Zseder',
version='0.1',
name='entity_extractor',
packages=['...'],
install_requires=['DAWG', 'mrjob', 'cchardet'],
package_dir={'': 'modules'},
scripts=['...'],
cmdclass={'install': MyInstall},
)
Мне нужно MyInstall
, потому что я хочу установить некоторые библиотеки из github, и я не хотел использовать параметр dependency_links
, потому что он обескуражен (например здесь), поэтому я могу сделать это с требованиями .txt.
Когда я устанавливаю этот пакет с помощью pip
, все работает нормально, но по некоторым причинам я должен решить это так, чтобы он также работал с чистым python setup.py install
. И это не так.
При переопределении cmdclass
в setup()
с моим собственным классом install_requires
, похоже, игнорируется. Как только я прокомментирую эту строку, эти пакеты устанавливаются.
Я знаю, что install_requires не поддерживается, например, в distutils (если я хорошо помню), но он находится в setuptools. И тогда cmdclass
не повлияет на install_requires
.
Я много часов искал эту проблему, нашел много родственных ответов на stackoverflow, но не для этой конкретной проблемы.
Приложив все необходимые пакеты к требованиям .txt, все работает нормально, но я хотел бы понять, почему это происходит. Спасибо!
Ответы
Ответ 1
То же самое случилось со мной. Кажется, что что-то запускает setuptools для установки "старого стиля" с distutils
, который действительно не поддерживает install_requires
.
Вы вызываете install.run(self), который вызывает run (self) в setuptools/setuptools/command/install.py, строка 51-74
https://bitbucket.org/pypa/setuptools/src/8e8c50925f18eafb7e66fe020aa91a85b9a4b122/setuptools/command/install.py?at=default
def run(self):
# Explicit request for old-style install? Just do it
if self.old_and_unmanageable or self.single_version_externally_managed:
return _install.run(self)
# Attempt to detect whether we were called from setup() or by another
# command. If we were called by setup(), our caller will be the
# 'run_command' method in 'distutils.dist', and *its* caller will be
# the 'run_commands' method. If we were called any other way, our
# immediate caller *might* be 'run_command', but it won't have been
# called by 'run_commands'. This is slightly kludgy, but seems to
# work.
#
caller = sys._getframe(2)
caller_module = caller.f_globals.get('__name__','')
caller_name = caller.f_code.co_name
if caller_module != 'distutils.dist' or caller_name!='run_commands':
# We weren't called from the command line or setup(), so we
# should run in backward-compatibility mode to support bdist_*
# commands.
_install.run(self)
else:
self.do_egg_install()
Я не уверен, предназначено ли это поведение, но вместо
install.run(self)
с
install.do_egg_install()
должен решить вашу проблему. По крайней мере, это работает для меня, но я также хотел бы получить более подробный ответ. Спасибо!
Ответ 2
В соответствии с fooobar.com/questions/140323/... более правильным способом сделать это может быть переопределение команды bdist_egg
.
Вы можете попробовать:
from setuptools.command.bdist_egg import bdist_egg as _bdist_egg
class bdist_egg(_bdist_egg):
def run(self):
call(["pip install -r requirements.txt --no-clean"], shell=True)
_bdist_egg.run(self)
...
setup(...
cmdclass={'bdist_egg': bdist_egg}, # override bdist_egg
)
Это сработало для меня, а install_require
больше не игнорируется. Тем не менее, я до сих пор не понимаю, почему большинство людей, кажется, переопределяют cmdclass install
и не жалуются на игнорирование install_require
.
Ответ 3
Я знаю, что это старый вопрос, но у меня возникла аналогичная проблема. Решение, которое я нашел, исправляет эту проблему для меня очень тонко: класс install
, который вы устанавливаете в cmd_class
, должен физически быть назван install
. См. этот ответ по соответствующей проблеме.
Обратите внимание, что я использую установку имени класса для моего производного класса, потому что это будет использовать python setup.py --help-commands.
Вы также должны использовать self.execute(_func_name, (), msg="msg")
в своем post_install вместо прямого вызова функции
Таким образом, реализация такого типа должна привести к тому, что вы избегаете обходного пути do_egg_install
, реализованного выше KEgg.
from setuptools.command.install import install as _install
...
def _post_install():
#code here
class install(_install):
def run(self):
_install.run(self)
self.execute(_post_install, (), msg="message here")