pypa / pip

The Python package installer
https://pip.pypa.io/
MIT License
9.52k stars 3.03k forks source link

Option to exclude scripts on install #3980

Open jaraco opened 8 years ago

jaraco commented 8 years ago

I'm trying to install a package but not install its scripts. The reason is I'd like to make the keyring package available in my Python 2 installation (for Mercurial) but continue to use keyring in my Python 3 installation as the default. If I python2 -m pip install keyring, it will overwrite the keyring script that links to the Python 3 installation.

Best I can tell, the two installs of this package will continue to fight over who owns keyring in /usr/local/bin.

I'd like to be able to exclude the script creation when installing on Python 2 to avoid conflicts.

As documented in the python-packaging-users-guide, easy_install allows excluding scripts from install, but pip does not.

I tried passing --install-options='--install-scripts=/dev/null' but that failed.

How do other people handle this situation?

What I've run:

$ python3 -m pip install keyring  
Collecting keyring
  Using cached keyring-9.3.1-py2.py3-none-any.whl
Installing collected packages: keyring
Successfully installed keyring-9.3.1
$ python2 -m pip install keyring
Collecting keyring
  Using cached keyring-9.3.1-py2.py3-none-any.whl
Installing collected packages: keyring
Successfully installed keyring-9.3.1
$ head -n 1 /usr/local/bin/keyring   
#!/usr/local/opt/python/bin/python2.7
$ python2 -m pip uninstall -y keyring
Uninstalling keyring-9.3.1:
  Successfully uninstalled keyring-9.3.1
$ ls -la /usr/local/bin/keyring      
ls: /usr/local/bin/keyring: No such file or directory

Here's where I tried using install-scripts:

$ python2 -m pip install keyring --install-option='--install-scripts=/dev/null'
/usr/local/lib/python2.7/site-packages/pip/commands/install.py:180: UserWarning: Disabling all use of wheels due to the use of --build-options / --global-options / --install-options.
  cmdoptions.check_install_build_global(options)
Collecting keyring
  Using cached keyring-9.3.1.tar.gz
Skipping bdist_wheel for keyring, due to binaries being disabled for it.
Installing collected packages: keyring
  Running setup.py install for keyring ... error
    Complete output from command /usr/local/opt/python/bin/python2.7 -u -c "import setuptools, tokenize;__file__='/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-build-9ULjAU/keyring/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-BMg4cF-record/install-record.txt --single-version-externally-managed --compile --install-scripts=/dev/null:
    running install
    running build
    running build_py
    creating build
    creating build/lib
    creating build/lib/keyring
    copying keyring/__init__.py -> build/lib/keyring
    copying keyring/__main__.py -> build/lib/keyring
    copying keyring/backend.py -> build/lib/keyring
    copying keyring/cli.py -> build/lib/keyring
    copying keyring/core.py -> build/lib/keyring
    copying keyring/credentials.py -> build/lib/keyring
    copying keyring/errors.py -> build/lib/keyring
    copying keyring/getpassbackend.py -> build/lib/keyring
    copying keyring/http.py -> build/lib/keyring
    copying keyring/py27compat.py -> build/lib/keyring
    copying keyring/py33compat.py -> build/lib/keyring
    creating build/lib/keyring/backends
    copying keyring/backends/__init__.py -> build/lib/keyring/backends
    copying keyring/backends/_OS_X_API.py -> build/lib/keyring/backends
    copying keyring/backends/fail.py -> build/lib/keyring/backends
    copying keyring/backends/kwallet.py -> build/lib/keyring/backends
    copying keyring/backends/OS_X.py -> build/lib/keyring/backends
    copying keyring/backends/SecretService.py -> build/lib/keyring/backends
    copying keyring/backends/Windows.py -> build/lib/keyring/backends
    creating build/lib/keyring/tests
    copying keyring/tests/__init__.py -> build/lib/keyring/tests
    copying keyring/tests/test_backend.py -> build/lib/keyring/tests
    copying keyring/tests/util.py -> build/lib/keyring/tests
    creating build/lib/keyring/util
    copying keyring/util/__init__.py -> build/lib/keyring/util
    copying keyring/util/escape.py -> build/lib/keyring/util
    copying keyring/util/platform_.py -> build/lib/keyring/util
    copying keyring/util/properties.py -> build/lib/keyring/util
    creating build/lib/keyring/tests/backends
    copying keyring/tests/backends/__init__.py -> build/lib/keyring/tests/backends
    copying keyring/tests/backends/test_kwallet.py -> build/lib/keyring/tests/backends
    copying keyring/tests/backends/test_OS_X.py -> build/lib/keyring/tests/backends
    copying keyring/tests/backends/test_SecretService.py -> build/lib/keyring/tests/backends
    copying keyring/tests/backends/test_Windows.py -> build/lib/keyring/tests/backends
    running egg_info
    writing requirements to keyring.egg-info/requires.txt
    writing dependency_links to keyring.egg-info/dependency_links.txt
    writing keyring.egg-info/PKG-INFO
    writing top-level names to keyring.egg-info/top_level.txt
    writing entry points to keyring.egg-info/entry_points.txt
    warning: manifest_maker: standard file '-c' not found

    reading manifest file 'keyring.egg-info/SOURCES.txt'
    writing manifest file 'keyring.egg-info/SOURCES.txt'
    running install_lib
    creating /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/__init__.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/__main__.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/backend.py -> /usr/local/lib/python2.7/site-packages/keyring
    creating /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/backends/__init__.py -> /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/backends/_OS_X_API.py -> /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/backends/fail.py -> /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/backends/kwallet.py -> /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/backends/OS_X.py -> /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/backends/SecretService.py -> /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/backends/Windows.py -> /usr/local/lib/python2.7/site-packages/keyring/backends
    copying build/lib/keyring/cli.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/core.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/credentials.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/errors.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/getpassbackend.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/http.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/py27compat.py -> /usr/local/lib/python2.7/site-packages/keyring
    copying build/lib/keyring/py33compat.py -> /usr/local/lib/python2.7/site-packages/keyring
    creating /usr/local/lib/python2.7/site-packages/keyring/tests
    copying build/lib/keyring/tests/__init__.py -> /usr/local/lib/python2.7/site-packages/keyring/tests
    creating /usr/local/lib/python2.7/site-packages/keyring/tests/backends
    copying build/lib/keyring/tests/backends/__init__.py -> /usr/local/lib/python2.7/site-packages/keyring/tests/backends
    copying build/lib/keyring/tests/backends/test_kwallet.py -> /usr/local/lib/python2.7/site-packages/keyring/tests/backends
    copying build/lib/keyring/tests/backends/test_OS_X.py -> /usr/local/lib/python2.7/site-packages/keyring/tests/backends
    copying build/lib/keyring/tests/backends/test_SecretService.py -> /usr/local/lib/python2.7/site-packages/keyring/tests/backends
    copying build/lib/keyring/tests/backends/test_Windows.py -> /usr/local/lib/python2.7/site-packages/keyring/tests/backends
    copying build/lib/keyring/tests/test_backend.py -> /usr/local/lib/python2.7/site-packages/keyring/tests
    copying build/lib/keyring/tests/util.py -> /usr/local/lib/python2.7/site-packages/keyring/tests
    creating /usr/local/lib/python2.7/site-packages/keyring/util
    copying build/lib/keyring/util/__init__.py -> /usr/local/lib/python2.7/site-packages/keyring/util
    copying build/lib/keyring/util/escape.py -> /usr/local/lib/python2.7/site-packages/keyring/util
    copying build/lib/keyring/util/platform_.py -> /usr/local/lib/python2.7/site-packages/keyring/util
    copying build/lib/keyring/util/properties.py -> /usr/local/lib/python2.7/site-packages/keyring/util
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/__init__.py to __init__.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/__main__.py to __main__.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backend.py to backend.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backends/__init__.py to __init__.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backends/_OS_X_API.py to _OS_X_API.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backends/fail.py to fail.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backends/kwallet.py to kwallet.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backends/OS_X.py to OS_X.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backends/SecretService.py to SecretService.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/backends/Windows.py to Windows.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/cli.py to cli.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/core.py to core.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/credentials.py to credentials.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/errors.py to errors.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/getpassbackend.py to getpassbackend.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/http.py to http.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/py27compat.py to py27compat.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/py33compat.py to py33compat.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/__init__.py to __init__.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/backends/__init__.py to __init__.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/backends/test_kwallet.py to test_kwallet.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/backends/test_OS_X.py to test_OS_X.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/backends/test_SecretService.py to test_SecretService.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/backends/test_Windows.py to test_Windows.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/test_backend.py to test_backend.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/tests/util.py to util.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/util/__init__.py to __init__.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/util/escape.py to escape.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/util/platform_.py to platform_.pyc
    byte-compiling /usr/local/lib/python2.7/site-packages/keyring/util/properties.py to properties.pyc
    running install_egg_info
    Copying keyring.egg-info to /usr/local/lib/python2.7/site-packages/keyring-9.3.1-py2.7.egg-info
    running install_scripts
    Installing keyring script to /dev/null
    error: [Errno 17] File exists: '/dev/null'

    ----------------------------------------
Command "/usr/local/opt/python/bin/python2.7 -u -c "import setuptools, tokenize;__file__='/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-build-9ULjAU/keyring/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-BMg4cF-record/install-record.txt --single-version-externally-managed --compile --install-scripts=/dev/null" failed with error code 1 in /private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-build-9ULjAU/keyring/
dstufft commented 7 years ago

I'm not sure I want a blanket option to to exclude scripts on install, but we do generally need to figure out something here.

RonnyPfannschmidt commented 7 years ago

random thought: perhaps the same logic as the --binary/--no-binary options can be used to opt out of scripts,

chrahunt commented 5 years ago

I usually handle this by creating a virtual environment, installing the application, and then creating a symlink in ~/bin that points to the generated script.

pipx could be another option, if pipxproject/pipx#86 gets addressed.

pradyunsg commented 5 years ago

Related: prevent pip from clobbering and overwriting files -- #4625

111pontes commented 4 years ago

This seems like a very convenient knob. Today, either the package gets split or the user needs to live with an unneeded/conflicting script.

lgarron commented 1 year ago

This would also be valuable for me. I maintain a program called folderify that was implemented in Python for a few years and therefore published to PyPI — but it's now deprecated. I currently have instructions for using an older version when needed:

pip3 install folderify
python3 -m folderify --macOS 10.5 path/to/icon.png

However, this is irresponsible because it will install a folderify binary that can (and does) clobber an installation of the latest implementation on any shared binary path. The latter installation will remain broken even if the user runs pip3 uninstall folderify immediately after.

I could resolve this by removing or renaming the folderify binary while keeping python3 -m folderify working (and I probably will[^1]). But for people that would like to keep using the old version using the same binary name as before (e.g. because they use it in other scripts and programs), this is a breaking change. By far the best compromise would be something like this:

pip3 install folderify --skip-install-scripts # or anything else that doesn't install the binary
python3 -m folderify --macOS 10.5 path/to/icon.png

Then the provided snippet doesn't clobber any binary, while allowing pip3 install folderify to work exactly like before for anybody who wants.

[^1]: EDIT: I did.

jhhcs commented 1 year ago

I'll pitch in with another user story here. I am maintaining binref/refinery, which is primarily a set of command-line utilities. It installs a lot of command-line scripts, because (duh) that's the default use case. However, there are also situations (mostly for automated malware analysis pipelines) where the ability to install it in a "library mode" without creating any scripts would be extremely useful.

sanmai-NL commented 1 year ago

This option would also reduce noise in --quiet logs, which wil show a warning for each and every executable that is installed through a Python packaged in a directory not in the PATH environment variable. A warning which is irrelevant in e.g., CI use cases.

adelplanque commented 9 months ago

Another use case, I have the same package installed in different versions of Python. I want to install the scripts only in one python version. Until now I was using

pythonx.y setup.py install --install-script=./trash

but I would like to use pip to follow the recommendations.