Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--install-option (in the cli) gets passed to deps being installed #1883

Open
s0undt3ch opened this issue Jun 18, 2014 · 24 comments
Open

--install-option (in the cli) gets passed to deps being installed #1883

s0undt3ch opened this issue Jun 18, 2014 · 24 comments
Labels
kind: backwards incompatible Would be backward incompatible project: setuptools Related to setuptools state: awaiting PR Feature discussed, PR is needed

Comments

@s0undt3ch
Copy link

$ pip install --install-option='--salt-root=/foo' salt
Downloading/unpacking salt
  Downloading salt-2014.1.5.tar.gz (2.8MB): 2.8MB downloaded
  Running setup.py (path:/tmp/ve-test/build/salt/setup.py) egg_info for package salt
    2014.1.5

    package init file 'salt/templates/__init__.py' not found (or not a regular file)
Downloading/unpacking Jinja2 (from salt)
  Downloading Jinja2-2.7.3.tar.gz (378kB): 378kB downloaded
  Running setup.py (path:/tmp/ve-test/build/Jinja2/setup.py) egg_info for package Jinja2

    warning: no files found matching '*' under directory 'custom_fixers'
    warning: no previously-included files matching '*' found under directory 'docs/_build'
    warning: no previously-included files matching '*.pyc' found under directory 'jinja2'
    warning: no previously-included files matching '*.pyc' found under directory 'docs'
    warning: no previously-included files matching '*.pyo' found under directory 'jinja2'
    warning: no previously-included files matching '*.pyo' found under directory 'docs'
Downloading/unpacking M2Crypto (from salt)
  Downloading M2Crypto-0.22.3.tar.gz (74kB): 74kB downloaded
  Running setup.py (path:/tmp/ve-test/build/M2Crypto/setup.py) egg_info for package M2Crypto

Downloading/unpacking msgpack-python (from salt)
  Downloading msgpack-python-0.4.2.tar.gz (114kB): 114kB downloaded
  Running setup.py (path:/tmp/ve-test/build/msgpack-python/setup.py) egg_info for package msgpack-python

Downloading/unpacking pycrypto (from salt)
  Downloading pycrypto-2.6.1.tar.gz (446kB): 446kB downloaded
  Running setup.py (path:/tmp/ve-test/build/pycrypto/setup.py) egg_info for package pycrypto

Downloading/unpacking PyYAML (from salt)
  Downloading PyYAML-3.11.tar.gz (248kB): 248kB downloaded
  Running setup.py (path:/tmp/ve-test/build/PyYAML/setup.py) egg_info for package PyYAML

Downloading/unpacking pyzmq>=2.2.0 (from salt)
  Downloading pyzmq-14.3.1.tar.gz (982kB): 982kB downloaded
  Running setup.py (path:/tmp/ve-test/build/pyzmq/setup.py) egg_info for package pyzmq

    no previously-included directories found matching 'docs/build'
    no previously-included directories found matching 'docs/gh-pages'
    warning: no previously-included files found matching 'bundled/zeromq/src/Makefile*'
    warning: no previously-included files found matching 'bundled/zeromq/src/platform.hpp'
    warning: no previously-included files found matching 'setup.cfg'
    warning: no previously-included files found matching 'zmq/libzmq*'
    warning: no previously-included files matching '__pycache__/*' found anywhere in distribution
    warning: no previously-included files matching '.deps/*' found anywhere in distribution
    warning: no previously-included files matching '*.so' found anywhere in distribution
    warning: no previously-included files matching '*.pyd' found anywhere in distribution
    warning: no previously-included files matching '.git*' found anywhere in distribution
    warning: no previously-included files matching '.DS_Store' found anywhere in distribution
    warning: no previously-included files matching '.mailmap' found anywhere in distribution
    warning: no previously-included files matching 'Makefile.am' found anywhere in distribution
    warning: no previously-included files matching 'Makefile.in' found anywhere in distribution
Downloading/unpacking MarkupSafe (from salt)
  Downloading MarkupSafe-0.23.tar.gz
  Running setup.py (path:/tmp/ve-test/build/MarkupSafe/setup.py) egg_info for package MarkupSafe

Downloading/unpacking apache-libcloud (from salt)
  Downloading apache_libcloud-0.14.1-py2.py3-none-any.whl (1.2MB): 1.2MB downloaded
Installing collected packages: salt, Jinja2, M2Crypto, msgpack-python, pycrypto, PyYAML, pyzmq, MarkupSafe, apache-libcloud
  Running setup.py install for salt
    2014.1.5
    package init file 'salt/templates/__init__.py' not found (or not a regular file)

    Installing salt-run script to /tmp/ve-test/bin
    Installing salt-minion script to /tmp/ve-test/bin
    Installing salt script to /tmp/ve-test/bin
    Installing salt-key script to /tmp/ve-test/bin
    Installing salt-call script to /tmp/ve-test/bin
    Installing salt-cp script to /tmp/ve-test/bin
    Installing salt-syndic script to /tmp/ve-test/bin
    Installing salt-ssh script to /tmp/ve-test/bin
    Installing salt-master script to /tmp/ve-test/bin
    Installing salt-cloud script to /tmp/ve-test/bin
  Running setup.py install for Jinja2
    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 --salt-root not recognized
    Complete output from command /tmp/ve-test/bin/python2 -c "import setuptools, tokenize;__file__='/tmp/ve-test/build/Jinja2/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-gFIT34-record/install-record.txt --single-version-externally-managed --compile --install-headers /tmp/ve-test/include/site/python2.7 --salt-root=/foo:
    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 --salt-root not recognized

----------------------------------------
Cleaning up...
Command /tmp/ve-test/bin/python2 -c "import setuptools, tokenize;__file__='/tmp/ve-test/build/Jinja2/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-gFIT34-record/install-record.txt --single-version-externally-managed --compile --install-headers /tmp/ve-test/include/site/python2.7 --salt-root=/foo failed with error code 1 in /tmp/ve-test/build/Jinja2
Storing debug log for failure in /home/vampas/.pip/pip.log
@holbech
Copy link

holbech commented Oct 15, 2014

I'm affected by this one as well. For now I'm resorting to ugly hacks using environment variables instead.

@johnyf
Copy link

johnyf commented Jul 13, 2015

Same here. What is the point of providing --install-option, if it will get passed to the dependencies and cause a failure ?
This makes it practically impossible to use --install-option for any package that has dependencies.

@johnyf
Copy link

johnyf commented Jul 13, 2015

It would be expected that --global-options are supposed to be passed to dependencies, but not --install-options. This is not the case in RequirementSet.install though.

@qwcode
Copy link
Contributor

qwcode commented Jul 16, 2015

starting with pip v7, pip supports using --install-option per line (i.e. per requirement) in a requirements file, which provides a solution for your need. in this use case, --install-option is not passed down to dependencies.

so like this

`pip install -r requirements.txt'

where requirements.txt contains

salt --install-option='--salt-root-dir=/foo'

using --install-option directly in the cli currently makes it global to the whole execution of pip, i.e. all top-level requirements and subdependencies.

I guess it could be changed to only apply to top-level requirements, but I'm not sure given the new support in requirements files.

i.e. supposing pip install --install-option='blah' pkg1 pkg2 pkg3, it would only apply to pkg1, pkg2, and pkg3, but not any dependencies.

@qwcode qwcode changed the title --install-option get's passed to deps being installed --install-option (in the cli) get's passed to deps being installed Jul 16, 2015
@johnyf
Copy link

johnyf commented Jul 16, 2015

Thanks a lot! I was aware of the newly introduced --install-option in requirement files, but after observing the command-line behavior, I assumed that it will be the same. This solves the main issue I had, which was to automate fetching a C library for compiling an extension of a dependency.

I cannot make this the default behavior (as it fetches a tarball external to PyPI, and also, the user may already have the library installed somewhere, and so want to set CFLAGS, LDFLAGS instead). So it has to be an extra option to setup.py. The only way to selectively pass an option to a dependency is through a requirements file, so no problem there.

The only other use case is when someone installs directly the package, and cannot pass this as a command line option via pip. Since this is a direct install, it wouldn't be very different to unpack the package and run setup.py directly, or to clone and do the same thing (after all, this seems to be the only way for passing compiler directives as environment variables).

A temporary workaround for this use case may be:

pip install foo
pip install --no-deps --force-reinstall --upgrade --install-option="--do-some-magic" foo

This should first install dependencies w/o passing any flags, then re-install the package only, passing --do-some-magic. (I added --upgrade, because it didn't work w/o it.)

@t-neumann
Copy link

Is the a resolution for that issue available yet?

This temporary workaround of first installing the package with dependencies and then re-installing the package with --install-option is not working for me:

Additional software is installed during the installation of the package and the --install-option could skip this step. But there's no use for this flag to simply install everything including the software first, just to then uninstall it again.

@pradyunsg pradyunsg added state: awaiting PR Feature discussed, PR is needed kind: backwards incompatible Would be backward incompatible type: bug A confirmed bug or unintended behavior labels Oct 2, 2017
@pradyunsg
Copy link
Member

I'm pretty sure that the only reason this has not been fixed is because no one has come around to fixing this.

@fpelliccioni
Copy link

Same problem here.
Does anyone know how to capture a --global-option from my setup.py?

MJJoyce added a commit to NASA-AMMOS/AIT-GUI that referenced this issue Mar 29, 2018
- Update README documentation with info on how to specify additional
PyPi servers so bliss-core can be installed as part of the GUI
installation. The developer install instructions had to be reworked due
to issues with pip passing install-option's to dependencies as well as
the package to be installed. The current supported work around is a
horrible hack. See pypa/pip#1883 for info.
- Updated the bliss-core version to the latest release.
@vcolano
Copy link

vcolano commented Apr 19, 2018

Going on 4 years this has been open and still no fix... any updates on when we might see some progress?

@ollamh
Copy link

ollamh commented Sep 27, 2018

Any updates on this one? For those who can't use requirements file this is crucial option to have.

@chrahunt
Copy link
Member

chrahunt commented Dec 11, 2019

Assuming we want to NOT pass options to dependencies by default (and possibly remove the ability entirely), one possible approach would be:

  1. Introduce a flag --pass-options-to-deps that makes pip behave the way it currently does. --no-pass-options-to-deps makes pip not pass options to dependencies.
  2. By default assume that we should pass options to dependencies, but if no explicit option was passed then print a deprecation warning stating that the behavior is changing in (if implemented right now) 20.2.
  3. In 20.2, change the default and remove the deprecation warning

If we want to remove the options getting passed to all dependencies behavior then the deprecation warning should also be traced if --pass-options-to-deps is used.

One reason we may want to remove the behavior entirely is to transition fully over to config settings (#5771), which we could incorporate this kind of configuration into.

@chrahunt chrahunt added project: setuptools Related to setuptools and removed type: bug A confirmed bug or unintended behavior labels Dec 11, 2019
@chrahunt
Copy link
Member

Removing bug label since this was almost certainly done intentionally initially for location-related options (like those deprecated in #7309).

anjakefala added a commit to chime-experiment/dias that referenced this issue May 11, 2020
- we want to use --install-options when installing dias
- bc of this pip bug pypa/pip#1883, we cannot
use that option in the same install command
- moving deps to requirements.txt splits it into two separate install
commands
anjakefala added a commit to chime-experiment/dias that referenced this issue May 12, 2020
- we want to use --install-options when installing dias
- bc of this pip bug pypa/pip#1883, we cannot
use that option in the same install command
- moving deps to requirements.txt splits it into two separate install
commands
anjakefala added a commit to chime-experiment/dias that referenced this issue May 19, 2020
sister PR: chime-experiment/ch_ansible#91

we previously used --install-options when installing dias
bc of this pip bug pypa/pip#1883, we could not use that option
    - it applied it to all of dias' dependencies
anjakefala added a commit to chime-experiment/dias that referenced this issue May 19, 2020
sister PR: chime-experiment/ch_ansible#91

we previously used --install-options when installing dias
bc of this pip bug pypa/pip#1883, we could not use that option
    - it applied it to all of dias' dependencies
@DiddiLeija
Copy link
Member

Hi everyone! How is this issue going? Is this still happening with newer versions of pip? If not, can we close it?

@johnyf
Copy link

johnyf commented Sep 15, 2021

This issue remains the same (#1883 (comment)). Passing an --install-option from the command line to pip results in the option being propagated to packages that are dependencies of the package given to pip. Then, when pip attempts to install those dependencies, the installation fails, because the dependencies do not recognize the --install-option. For example:

pip install --verbose dd --install-option='--cudd' --install-option='--cudd_zdd'

raises an error (below intermediate output has been replaced with [...], and parts of paths replaced with <...>):

/<...>/.virtualenvs/py39_7/lib/python3.9/site-packages/pip/_internal/commands/install.py:229: UserWarning: Disabling all use of wheels due to the use of --build-option / --global-option / --install-option.
  cmdoptions.check_install_build_global(options)
Using pip 21.2.4 from /<...>/.virtualenvs/py39_7/lib/python3.9/site-packages/pip (python 3.9)
Collecting dd
  Using cached dd-0.5.6.tar.gz (639 kB)

[...]

Installing collected packages: pyparsing, ply, pydot, psutil, astutils, dd
    Running command /<...>/.virtualenvs/py39_7/bin/python -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/<...>/pip-install-3mq4ucs1/pyparsing_79f31590008e46d5a1d51adbd262851c/setup.py'"'"'; __file__='"'"'/<...>/pip-install-3mq4ucs1/pyparsing_79f31590008e46d5a1d51adbd262851c/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /<...>/pip-record-xoxr6gev/install-record.txt --single-version-externally-managed --compile --install-headers /<...>/.virtualenvs/py39_7/include/site/python3.9/pyparsing --cudd --cudd_zdd
    usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
       or: setup.py --help [cmd1 cmd2 ...]
       or: setup.py --help-commands
       or: setup.py cmd --help

    error: option --cudd not recognized
    Running setup.py install for pyparsing ... error
ERROR: Command errored out with exit status 1: /<...>/.virtualenvs/py39_7/bin/python -u -c 'import io, os, sys, setuptools, tokenize; sys.argv[0] = '"'"'/<...>/pip-install-3mq4ucs1/pyparsing_79f31590008e46d5a1d51adbd262851c/setup.py'"'"'; __file__='"'"'/<...>/pip-install-3mq4ucs1/pyparsing_79f31590008e46d5a1d51adbd262851c/setup.py'"'"';f = getattr(tokenize, '"'"'open'"'"', open)(__file__) if os.path.exists(__file__) else io.StringIO('"'"'from setuptools import setup; setup()'"'"');code = f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /<...>/pip-record-xoxr6gev/install-record.txt --single-version-externally-managed --compile --install-headers /<...>/.virtualenvs/py39_7/include/site/python3.9/pyparsing --cudd --cudd_zdd Check the logs for full command output.

@johnyf
Copy link

johnyf commented Sep 15, 2021

The issue can be addressed by using process substitution:

pip install --verbose \
    -r <(echo "dd --install-option='--fetch' --install-option='--cudd' --install-option='--cudd_zdd'")

This works, because it emulates a requirements file, and thus changes the scope of --install-option (the options then do not propagate to dependencies: #1883 (comment)).

This approach is much better than the install-then-reinstall approach that I described above (#1883 (comment)).

Also, the install-then-reinstall approach is possible for those packages that have a pure Python variant, as well as an optimized alternative (i.e., same API implemented in both pure Python and using built extensions, e.g., Cython). The package dd is such an example.

For packages that necessarily need --install-option to be installed, process substitution addresses the issue (whereas install-then-reinstall would not).

This approach works also for installing from the local directory:

pip install --verbose --use-feature=in-tree-build \
    -r <(echo ". --install-option='--fetch' --install-option='--cudd' --install-option='--cudd_zdd'")

@johnyf
Copy link

johnyf commented Oct 16, 2021

As of pip == 21.3, the above (#1883 (comment)) call for installing from the local directory becomes:

pip install --verbose \
    -r <(echo ". --install-option='--fetch' --install-option='--cudd' --install-option='--cudd_zdd'")

@johnyf
Copy link

johnyf commented Nov 21, 2021

Process substitution (suggested above: #1883 (comment)) is not in POSIX. For example, it is not available in /bin/sh, and so not inside makefiles, unless workarounds are used. Environment variables are another option, when process substitution is unavailable, to emulate the requirements-file behavior of --install-option, from the command-line. This observation motivates having the command-line --install-option behave the same way that the requirements-file --install-option behaves.

(It seems useful to also allow multiple command-line parameters and arguments to be passed within the argument of a single occurrence of --install-option, so that the users would need to write --install-option fewer times on each invocation of pip install. Perhaps shlex is relevant to this possibility.)

@wudstrand
Copy link

Any update on this issue?

@sbidoul
Copy link
Member

sbidoul commented Aug 4, 2022

It is highly unlikely that we will change this behaviour, since the --install-option and --global-option force pip to use setup.py's install command, which is deprecated.

As a temporary workaround, you can try creating a requirements.in file with one line:

salt --install-option='--salt-root=/foo'

and install with pip install -r requirements.in.

OTOH, now might be a good time to confirm this is the behaviour we want for --config-settings too. cc/ @pypa/pip-committers.

@pfmoore
Copy link
Member

pfmoore commented Aug 4, 2022

In my view, yes, we should continue with the current behaviour:

  1. Specifying config settings on the command line should apply to all builds by default
  2. Specifying config settings in a requirements file should apply just to the given requirement, to give users a way to specify requirements on a per-user basis

The first case is because otherwise, how would you specify an option that applies to everything? The second is because that is how any per-requirement options are set currently.

Any other approach would need justifying (as it's a deviation from how we handle every similar option) and given that the semantics of config options are currently entirely backend-dependent, we can't realistically make such a judgement. If there's a case for a different approach, it should be made on Discourse, so that all backend maintainers have a chance to get involved, and any conclusions can be properly recorded (as standards, if necessary).

@pradyunsg
Copy link
Member

@sbidoul
Copy link
Member

sbidoul commented Aug 4, 2022

how would you specify an option that applies to everything

Personally I'm not quite sure it makes sense to pass the same config settings to all builds. But I lack concrete use cases to help form an opinion.

@uranusjr
Copy link
Member

uranusjr commented Aug 4, 2022

For --config-settings (and the legacy --build-option), automatically cascading to dependencies actually is the default for a lot of build systems out there. This kind of things is usually for build-time flags, and for a lot of those, you actually need the flags to be cascaded, otherwise your dependencies won’t link correctly. So the general solution in these build systems is generally tell packagers they should be careful to handle unknown options correctly, which I feel should also be how we approach this.

@pfmoore
Copy link
Member

pfmoore commented Aug 4, 2022

https://discuss.python.org/c/packaging/14

Broken link?

But I lack concrete use cases to help form an opinion.

So do I. As @uranusjr says, other build systems cascade by default (as does the common "set environment variables to configure your compiler" model). But it's not clear that config options in Python will correspond to that sort of option. Until build backends start making use of config options, we're guessing.

As far as I know, only setuptools implements config options:

  1. For configuring the editable mode. I have no feel for whether this makes sense to cascade, but my first guess is that it does no harm to cascade it. Dependencies aren't editable, after all.
  2. As a way to pass the historical build option flags - for which "preserve existing behaviour" is certainly as good as we have now, and possibly as good as we can get, given that setuptools lets projects use any semantics they want for build options. Given the original issue raised here, maybe setuptools should default to ignoring unknown build options1, rather than failing, but that's a matter for them.

Footnotes

  1. Which is how config options work.

@uranusjr uranusjr changed the title --install-option (in the cli) get's passed to deps being installed --install-option (in the cli) gets passed to deps being installed Dec 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: backwards incompatible Would be backward incompatible project: setuptools Related to setuptools state: awaiting PR Feature discussed, PR is needed
Projects
None yet
Development

No branches or pull requests