可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is it possible to specify a post-install Python script file as part of the setuptools setup.py file so that a user can run the command:
python setup.py install
on a local project file archive, or
pip install
for a PyPI project and the script will be run at the completion of the standard setuptools install? I am looking to perform post-install tasks that can be coded in a single Python script file (e.g. deliver a custom post-install message to the user, pull additional data files from a different remote source repository).
I came across this SO answer from several years ago that addresses the topic and it sounds as though the consensus at that time was that you need to create an install subcommand. If that is still the case, would it be possible for someone to provide an example of how to do this so that it is not necessary for the user to enter a second command to run the script?
回答1:
This solution is more transparent:
You will make a few additions to setup.py and there is no need to an extra file.
Also you need to consider two different post-installations; one for development/editable mode and the other one for install mode.
Add these two classes that includes your post-install script to setup.py:
from setuptools import setup from setuptools.command.develop import develop from setuptools.command.install import install class PostDevelopCommand(develop): """Post-installation for development mode.""" def run(self): # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION develop.run(self) class PostInstallCommand(install): """Post-installation for installation mode.""" def run(self): # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION install.run(self)
and insert cmdclass
argument to setup()
function in setup.py:
setup( ... cmdclass={ 'develop': PostDevelopCommand, 'install': PostInstallCommand, }, ... )
You can even call shell commands during post-installation, like this:
from setuptools import setup from setuptools.command.develop import develop from setuptools.command.install import install from subprocess import check_call class PostDevelopCommand(develop): """Post-installation for development mode.""" def run(self): check_call("apt-get install this-package".split()) develop.run(self) class PostInstallCommand(install): """Post-installation for installation mode.""" def run(self): check_call("apt-get install this-package".split()) install.run(self) setup( ...
P.S. there is no any pre-install entry points available on setuptools. Read this discussion if you are wondering why there is none.
回答2:
This is the only strategy that has worked for me when the post-install script requires that the package dependencies have already been installed:
import atexit from setuptools.command.install import install def _post_install(): print('POST INSTALL') class new_install(install): def __init__(self, *args, **kwargs): super(new_install, self).__init__(*args, **kwargs) atexit.register(_post_install) setuptools.setup( cmdclass={'install': new_install},
回答3:
A solution could be to include a post_setup.py
in setup.py
's directory. post_setup.py
will contain a function which does the post-install and setup.py
will only import and launch it at the appropriate time.
In setup.py
:
from distutils.core import setup from distutils.command.install_data import install_data try: from post_setup import main as post_install except ImportError: post_install = lambda: None class my_install(install_data): def run(self): install_data.run(self) post_install() if __name__ == '__main__': setup( ... cmdclass={'install_data': my_install}, ... )
In post_setup.py
:
def main(): """Do here your post-install""" pass if __name__ == '__main__': main()
With the common idea of launching setup.py
from its directory, you will be able to import post_setup.py
else it will launch an empty function.
In post_setup.py
, the if __name__ == '__main__':
statement allows you to manually launch post-install from command line.
回答4:
Combining the answers from @Apalala, @Zulu and @mertyildiran; this worked for me in a Python 3.5 environment:
import atexit import os import sys from setuptools import setup from setuptools.command.install import install class CustomInstall(install): def run(self): def _post_install(): def find_module_path(): for p in sys.path: if os.path.isdir(p) and my_name in os.listdir(p): return os.path.join(p, my_name) install_path = find_module_path() # Add your post install code here atexit.register(_post_install) install.run(self) setup( cmdclass={'install': CustomInstall}, ...
This also gives you access the to the installation path of the package in install_path
, to do some shell work on.