21

I want to make my python package "pip installable". The problem is that the package has shell script that must be sourced in the user's init shell script (e.g. .bashrc).

But after the installation, the user don't exactly know where the script went (presumably /usr/bin, but we can't garantee). Of course the user can runs which myscript.sh and manually edits his init script.

But I want to automate this step. I can create a new distutils command, but pip install doesn't call it. And I can extend distutils.command.install.install, but the installation breaks via pip (although works via python setup.py install):

setup.py

from distutils.command.install import install

class CustomInstall(install):
    def run(self):
        install.run(self)
        # custom stuff here
        do_my_stuff()

setup(..., cmdclass={'install': CustomInstall})

shell

$ pip install dist/mypackage.tar.gz
Unpacking ./dist/mypackage.tar.gz
  Running setup.py egg_info for package from file:///path/to/mypackage/dist/mypackage.tar.gz

Installing collected packages: mypackage
  Running setup.py install for mypackage
    usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
       or: -c --help [cmd1 cmd2 ...]
       or: -c --help-commands
       or: -c cmd --help

    error: option --single-version-externally-managed not recognized
    Complete output from command /path/to/.virtualenvs/myvirtualenv/bin/python -c "import setuptools;__file__='/tmp/pip-OFjrqU-build/setup.py';exec(compile(open(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-s4Yo4d-record/install-record.txt --single-version-externally-managed --install-headers /path/to/.virtualenvs/myvirtualenv/include/site/python2.7:
    usage: -c [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
       or: -c --help [cmd1 cmd2 ...]
       or: -c --help-commands
       or: -c cmd --help

error: option --single-version-externally-managed not recognized

----------------------------------------
Command /path/to/.virtualenvs/myvirtualenv/bin/python -c "import setuptools;__file__='/tmp/pip-OFjrqU-build/setup.py';exec(compile(open(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-s4Yo4d-record/install-record.txt --single-version-externally-managed --install-headers /path/to/.virtualenvs/myvirtualenv/include/site/python2.7 failed with error code 1 in /tmp/pip-OFjrqU-build
Storing complete log in /path/to/myhome/.pip/pip.log

What is the best aproach to run a custom task after install a package via pip?

akaihola
  • 26,309
  • 7
  • 59
  • 69
borges
  • 3,627
  • 5
  • 29
  • 44

2 Answers2

15

Could you try with from setuptools.command.install import install instead of using distutils?

Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • Thanks, this really helped me out. I had followed (https://stackoverflow.com/questions/17806485/execute-a-python-script-post-install-using-distutils-setuptools) and received the same issue, documented it in (http://blog.diffbrent.com/correctly-adding-nltk-to-your-python-package-using-setup-py-post-install-commands/) and gave you props – brent.payne Sep 17 '14 at 00:17
  • 6
    I'm getting the same problem as [here](http://stackoverflow.com/questions/19569557/pip-not-picking-up-a-custom-install-cmdclass?lq=1) - I get the custom behaviour when running `python setup.py install`, but not when I run `pip install `. Any thoughts? – scubbo May 27 '15 at 22:25
  • 8
    I have the same issue. ```pip install -e .``` runs the command class and then it runs when I do bdist_wheel but during pip install nothing happens. – bjorns Aug 31 '15 at 02:29
  • 1
    @scubbo did you ever arrive at a solution to this? – Alex Jun 20 '17 at 14:22
  • 1
    I did not, sorry! This was a while ago, so I'd need to ramp back up to re-understand the issue - hopefully one of the other commenters can help you out, though? – scubbo Jun 20 '17 at 16:54
1

I update the code to work with the

pip install <path>

as well. In this example, we create a directory structure during installation. All this goes into the setup.py file, followed by $ pip install . This solution also answers this related post: Custom pip install commands not running

from setuptools import setup
from setuptools.command.install import install
import pip
import os, errno

my_dir_paths = ['1', '2', '3']

class CustomInstall(install):

    def __init__(self, dist):
        super(install, self).__init__(dist)
        self.__current  = "/home/..."
        self.__post_install(self.__current)
        
    def run(self):
        install.run(self)
        
    def __post_install(self, curr):
        try:
            os.makedirs(os.path.join(curr, "data2"))
        except FileExistsError:
            print("Directory %s already exists!"%("data2"))
            pass

        for path in my_dir_paths:
            try:
                os.makedirs(os.path.join(curr, "data2", path))
            except FileExistsError:
                print("Directory %s already exists!"%(path))
                pass



setup(
    name='some_name',
    version='0.1.0',
    author='me',
    author_email='...',
    url='http://...',
    packages=setuptools.find_packages(),
    license='...',
    description='....',
    long_description=open('README.txt').read(),
    install_requires=[
       "pandas >= 1.0.5",
    ],
    python_requires='>=3.6',
    
    cmdclass={'install': CustomInstall}
    
)

Mihai.Mehe
  • 448
  • 8
  • 13