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

Disallow explicitly passing install-location-related arguments in --install-options #7309

Closed
chrahunt opened this issue Nov 7, 2019 · 22 comments · Fixed by #8556
Closed
Labels
C: cli Command line interface related things (optparse, option grouping etc) kind: backwards incompatible Would be backward incompatible project: setuptools Related to setuptools !release blocker Hold a release until this is resolved type: deprecation Related to deprecation / removal.
Milestone

Comments

@chrahunt
Copy link
Member

chrahunt commented Nov 7, 2019

What's the problem this feature will solve?

Currently the --install-option parameter allows passing arbitrary arguments to setup.py for packages using the legacy-installation path. If any options/flags that control install location are passed, such as:

  • --exec-prefix
  • --home
  • --install-base
  • --install-data
  • --install-headers
  • --install-lib
  • --install-platlib
  • --install-purelib
  • --install-scripts
  • --prefix
  • --root
  • --user

then it causes issues. Not every package uses setup.py, so in the general case this causes legacy packages to be installed with the the scheme provided in --install-option and PEP 517/wheel packages to be installed with the scheme pip is aware of. This has already hit two users (#7253, #7240).

Pip knows how to use scheme information properly for PEP 517, wheel, and legacy installs. Enforcing that pip is the only one passing this kind of information will make the user experience more consistent.

Describe the solution you'd like

Explicitly fail pip install if --install-option includes any of the above arguments, whether provided on the command-line or in a requirements file.

In addition to the consistent user experience, this would also allow pip to pass the more explicit

  • --install-{purelib,platlib,headers,scripts,data} (for install)
  • --install-dir, --script-dir (for install -e)

to setup.py instead of the current --prefix, --home, --root, --user, and --install-headers.

This may also be required to implement PEP 582 (__pypackages__) since doing legacy installs to a plain lib directory isn't possible with the current arguments being passed to setup.py, and passing an explicit --install-lib would break any usages of the other arguments in --install-option.

IMO this could be implemented without deprecation because the current behavior demonstrated in #7253 and #7240 causes failures in non-obvious ways, and the remediation is simple in most cases - just pass the parameters to pip instead.

Alternative Solutions

  • do nothing - as we accumulate more features that conflict with these arguments being passed to --install-option we will get more support tickets. These will taper off eventually as people stop using these flags in order to install non-legacy packages. We eventually forget that the user can pass these options, and implement new features without worrying about it.
  • automatically determine a scheme based on --install-option - this defeats the purpose of having pip-level arguments
  • only fail the install if the scheme in --install-option conflicts with the one specified at the pip level - this is more friendly, but I feel that it would also be more confusing and add to the implementation burden in pip (reconciling --install-option across different packages in a requirements file)

Additional context

@chrahunt chrahunt added kind: backwards incompatible Would be backward incompatible project: setuptools Related to setuptools C: cli Command line interface related things (optparse, option grouping etc) labels Nov 7, 2019
@pradyunsg
Copy link
Member

Sounds like a good idea to me.

We'd have to go through a regular deprecation cycle here but I don't see any reason to not do this.

@chrahunt
Copy link
Member Author

chrahunt commented Nov 7, 2019

I would argue that this is an extension of #6606/fix for #7240, and thus doesn't require a deprecation cycle. As mentioned above, the current behavior when using these flags is broken unless all builds are going through the legacy code path.

@pradyunsg
Copy link
Member

pradyunsg commented Nov 7, 2019

nods

I don't think this is an extension of #6606 -- that PR changed when pip used PEP 517, which is something we're still working on. This, however, affects everyone on the legacy code path. We'd be removing functionality that user might depend on directly, so we should go through the deprecation cycle for it.

FWIW, I now know that basically any change in pip's build logic will affect someone (i.e. break their deployment / CI pipeline) and they're usually not happy about it. With the power of retrospect, I feel we should've gone for at least short deprecation cycle (1 release) for #6606 since that was a (minor) change in end-user-affecting behavior as well.

@chrahunt
Copy link
Member Author

chrahunt commented Nov 7, 2019

OK. After thinking on it more, we can communicate the workaround in the deprecation notice. Hopefully that means currently impacted users will not have to search the issue tracker when some packages are missing, so I don't mind that approach as much as I did initially. It might even be worth including in a fix release for 19.3? Then we could also close #7240.

@pradyunsg
Copy link
Member

Given that we've already closed that issue (which is a form of communication on our release processes), I'm inclined to say no -- but it's really an RM's decision so... pinging @xavfernandez! :P

FWIW, I'm in no hurry to close #7240 but yes, even the deprecation messaging is enough to justify closing it.

@xavfernandez
Copy link
Member

I'm on board with forbidding conflicting options in --install-options and also in favor of a deprecation period.
Concerning a 19.3.2 release, it seems strange to make a patch release just to include a warning... but if everyone agrees (and if the patch is clean), why not :)

@chrahunt
Copy link
Member Author

chrahunt commented Nov 7, 2019

Note that #7002 (unreleased) will also break some usages of these options in --install-option, since we only take into account the --user, --prefix, --target, and --root options explicitly passed to pip when determining whether to do a user install.

@chrahunt
Copy link
Member Author

chrahunt commented Nov 9, 2019

After talking this over with @pradyunsg, our conclusions are:

  1. we should maintain the 6 month deprecation period for this (so if we make a patch release or get this in 20.0, we remove in 20.2)
  2. Fix PEP 517 builds for packages without setup.py #6606 and Default to --user install in certain conditions #7002 should not be reverted, and we can treat the deprecation notice as a warning that things may not work as expected (with wheels and automatically-determined user-mode installation)
  3. in the interim, we may be able to distill the options passed to --install-option into input for our scheme (potentially blocking if there is some conflict between --install-option and arguments passed directly to pip), which would let us press forward with other refactoring that requires pip to have more control over the setuptools arguments

@chrahunt
Copy link
Member Author

chrahunt commented Dec 1, 2019

For the more fine-grained option handling, I'm thinking the following:

install options mapped to pip options

if provided in any --install-option, these options would be compared against all other requirements' --install-options and the options provided to pip itself. If the pip equivalents are unset then they would be set to the provided options and the options removed from --install-option. If the pip equivalents are set to a different value, then we raise an error. If other requirements have a different value or do not have the same value in their --install-options then we raise an error.

  1. --home maps to --target
  2. --prefix maps to --prefix
  3. --root maps to --root
  4. --user maps to --user

install options that override scheme values directly

If provided in any --install-option, these options would be compared against all other requirements' --install-options. If the other requirements have a different value or do not have the same value in their --install-option then we raise an error. If there are any pip-level location options then we raise an error. Otherwise we explicitly set the corresponding field on our install scheme after we generate the default one and remove the argument from --install-option.

  1. --install-data
  2. --install-headers
  3. --install-lib
  4. --install-platlib
  5. --install-purelib
  6. --install-scripts

unsupported install options

These options, which do not have an equivalent in the scheme that we install packages to, are unsupported and if they are provided in any --install-options then we raise an error

  1. --exec-prefix
  2. --install-base

I think this is the minimum (in terms of user-facing impact) we can do while still being able to move forward on creating an explicit scheme for install.

Just to reiterate why this is important: once the above is in place, we would be able to do the following:

  1. reconcile input arguments
  2. map input arguments to a Scheme object in InstallCommand.run
  3. use that Scheme to instantiate a working set
  4. use that working set for finding installed distributions during dependency resolution (cleaning up a lot of Resolver/RequirementPreparer/InstallRequirement in the process)
  5. use that scheme as the basis for installation (and conflicting requirement uninstallation), which should fix several bugs (pip install -U does not remember whether a package was installed with --user #1056, pip install --user -e tries to uninstall system-wide version, unlike pip install --user #4296, Provide a --target option on uninstall (to undo install with --target) #5595)

@chrahunt chrahunt added the type: deprecation Related to deprecation / removal. label Dec 12, 2019
skytreader added a commit to skytreader/virtualenv-burrito that referenced this issue May 22, 2020
This future-proofs this script as this form of invocation is about to go the way
of dinosaurs. See pypa/pip#7309.
@sbidoul sbidoul added this to the 20.2 milestone May 24, 2020
@sbidoul
Copy link
Member

sbidoul commented May 24, 2020

Added to milestone 20.2.

@pradyunsg
Copy link
Member

Was a deprecation warning added in 19.3/20.2? Or is this an optimistic "we'll add the warning in 20.2"?

@sbidoul
Copy link
Member

sbidoul commented May 24, 2020

@pradyunsg There is a deprecation warning pointing to this issue, with gone_in="20.2". No feedback was reported here so I assume it's now appropriate to convert the warning into an error.

@pradyunsg
Copy link
Member

Awesome!

@pradyunsg
Copy link
Member

The relevant bit of code:

deprecated(
reason=(
"Location-changing options found in --install-option: {}. "
"This configuration may cause unexpected behavior and is "
"unsupported.".format(
"; ".join(offenders)
)
),
replacement=(
"using pip-level options like --user, --prefix, --root, and "
"--target"
),
gone_in="20.2",
issue=7309,
)

@pradyunsg pradyunsg added the !release blocker Hold a release until this is resolved label Jul 2, 2020
@chrahunt
Copy link
Member Author

chrahunt commented Jul 9, 2020

So, unfortunately, I didn't include --install-dir and --script-dir in the list of things to get checked. This is supported by its usage in this test, which started to fail when I refactored editable installation to explicitly pass --script-dir to setup.py. These values are only relevant to editable installs.

As a result we can't do a full replacement now like I was intending. We can still take an approach like I described in #7309 (comment), and it should be quite a bit easier since there are only two values and they map directly to the scheme values we should use (overriding "scripts" and "lib_dir"). We could do that while waiting another 2 releases before disallowing --install-dir and --script-dir and still get the majority of the benefits.

bors bot referenced this issue in duckinator/emanate Jul 30, 2020
153: Update pip to 20.2 r=duckinator a=pyup-bot


This PR updates [pip](https://pypi.org/project/pip) from **20.1.1** to **20.2**.



<details>
  <summary>Changelog</summary>
  
  
   ### 20.2
   ```
   =================

Deprecations and Removals
-------------------------

- Deprecate setup.py-based builds that do not generate an ``.egg-info`` directory. (`6998 &lt;https://github.com/pypa/pip/issues/6998&gt;`_, `8617 &lt;https://github.com/pypa/pip/issues/8617&gt;`_)
- Disallow passing install-location-related arguments in ``--install-options``. (`7309 &lt;https://github.com/pypa/pip/issues/7309&gt;`_)
- Add deprecation warning for invalid requirements format &quot;base&gt;=1.0[extra]&quot; (`8288 &lt;https://github.com/pypa/pip/issues/8288&gt;`_)
- Deprecate legacy setup.py install when building a wheel failed for source
  distributions without pyproject.toml (`8368 &lt;https://github.com/pypa/pip/issues/8368&gt;`_)
- Deprecate -b/--build/--build-dir/--build-directory. Its current behaviour is confusing
  and breaks in case different versions of the same distribution need to be built during
  the resolution process. Using the TMPDIR/TEMP/TMP environment variable, possibly
  combined with --no-clean covers known use cases. (`8372 &lt;https://github.com/pypa/pip/issues/8372&gt;`_)
- Remove undocumented and deprecated option ``--always-unzip`` (`8408 &lt;https://github.com/pypa/pip/issues/8408&gt;`_)

Features
--------

- Log debugging information about pip, in ``pip install --verbose``. (`3166 &lt;https://github.com/pypa/pip/issues/3166&gt;`_)
- Refine error messages to avoid showing Python tracebacks when an HTTP error occurs. (`5380 &lt;https://github.com/pypa/pip/issues/5380&gt;`_)
- Install wheel files directly instead of extracting them to a temp directory. (`6030 &lt;https://github.com/pypa/pip/issues/6030&gt;`_)
- Add a beta version of pip&#39;s next-generation dependency resolver.

  Move pip&#39;s new resolver into beta, remove the
  ``--unstable-feature=resolver`` flag, and enable the
  ``--use-feature=2020-resolver`` flag. The new resolver is
  significantly stricter and more consistent when it receives
  incompatible instructions, and reduces support for certain kinds of
  :ref:`Constraints Files`, so some workarounds and workflows may
  break. More details about how to test and migrate, and how to report
  issues, at :ref:`Resolver changes 2020` . Maintainers are preparing to
   ```
   
  
  
   ### 20.2b1
   ```
   ===================

Bug Fixes
---------

- Correctly treat wheels containing non-ASCII file contents so they can be
  installed on Windows. (`5712 &lt;https://github.com/pypa/pip/issues/5712&gt;`_)
- Prompt the user for password if the keyring backend doesn&#39;t return one (`7998 &lt;https://github.com/pypa/pip/issues/7998&gt;`_)

Improved Documentation
----------------------

- Add GitHub issue template for reporting when the dependency resolver fails (`8207 &lt;https://github.com/pypa/pip/issues/8207&gt;`_)
   ```
   
  
</details>


 

<details>
  <summary>Links</summary>
  
  - PyPI: https://pypi.org/project/pip
  - Changelog: https://pyup.io/changelogs/pip/
  - Homepage: https://pip.pypa.io/
</details>



Co-authored-by: pyup-bot <github-bot@pyup.io>
bors bot referenced this issue in duckinator/emanate Jul 31, 2020
153: Update pip to 20.2 r=duckinator a=pyup-bot


This PR updates [pip](https://pypi.org/project/pip) from **20.1.1** to **20.2**.



<details>
  <summary>Changelog</summary>
  
  
   ### 20.2
   ```
   =================

Deprecations and Removals
-------------------------

- Deprecate setup.py-based builds that do not generate an ``.egg-info`` directory. (`6998 &lt;https://github.com/pypa/pip/issues/6998&gt;`_, `8617 &lt;https://github.com/pypa/pip/issues/8617&gt;`_)
- Disallow passing install-location-related arguments in ``--install-options``. (`7309 &lt;https://github.com/pypa/pip/issues/7309&gt;`_)
- Add deprecation warning for invalid requirements format &quot;base&gt;=1.0[extra]&quot; (`8288 &lt;https://github.com/pypa/pip/issues/8288&gt;`_)
- Deprecate legacy setup.py install when building a wheel failed for source
  distributions without pyproject.toml (`8368 &lt;https://github.com/pypa/pip/issues/8368&gt;`_)
- Deprecate -b/--build/--build-dir/--build-directory. Its current behaviour is confusing
  and breaks in case different versions of the same distribution need to be built during
  the resolution process. Using the TMPDIR/TEMP/TMP environment variable, possibly
  combined with --no-clean covers known use cases. (`8372 &lt;https://github.com/pypa/pip/issues/8372&gt;`_)
- Remove undocumented and deprecated option ``--always-unzip`` (`8408 &lt;https://github.com/pypa/pip/issues/8408&gt;`_)

Features
--------

- Log debugging information about pip, in ``pip install --verbose``. (`3166 &lt;https://github.com/pypa/pip/issues/3166&gt;`_)
- Refine error messages to avoid showing Python tracebacks when an HTTP error occurs. (`5380 &lt;https://github.com/pypa/pip/issues/5380&gt;`_)
- Install wheel files directly instead of extracting them to a temp directory. (`6030 &lt;https://github.com/pypa/pip/issues/6030&gt;`_)
- Add a beta version of pip&#39;s next-generation dependency resolver.

  Move pip&#39;s new resolver into beta, remove the
  ``--unstable-feature=resolver`` flag, and enable the
  ``--use-feature=2020-resolver`` flag. The new resolver is
  significantly stricter and more consistent when it receives
  incompatible instructions, and reduces support for certain kinds of
  :ref:`Constraints Files`, so some workarounds and workflows may
  break. More details about how to test and migrate, and how to report
  issues, at :ref:`Resolver changes 2020` . Maintainers are preparing to
   ```
   
  
  
   ### 20.2b1
   ```
   ===================

Bug Fixes
---------

- Correctly treat wheels containing non-ASCII file contents so they can be
  installed on Windows. (`5712 &lt;https://github.com/pypa/pip/issues/5712&gt;`_)
- Prompt the user for password if the keyring backend doesn&#39;t return one (`7998 &lt;https://github.com/pypa/pip/issues/7998&gt;`_)

Improved Documentation
----------------------

- Add GitHub issue template for reporting when the dependency resolver fails (`8207 &lt;https://github.com/pypa/pip/issues/8207&gt;`_)
   ```
   
  
</details>


 

<details>
  <summary>Links</summary>
  
  - PyPI: https://pypi.org/project/pip
  - Changelog: https://pyup.io/changelogs/pip/
  - Homepage: https://pip.pypa.io/
</details>



Co-authored-by: pyup-bot <github-bot@pyup.io>
Co-authored-by: Ellen Marie Dash <me@duckie.co>
prafullkotecha added a commit to direkshan-digital/gcp-microservices-demo that referenced this issue Aug 2, 2020
@BastienFaure
Copy link

@chrahunt I've been looking along this thread and haven't been able to find my answer, hopefuly you can give me an answer :)
I previously legeraged --install-option="--install-scripts=/custom/path/" to deploy only scripts to a specific location but this is now rejected with the recents updates.
Is there still a way through pip to install scripts to a custom location without moving the whole package ?

Thank you

@chrahunt
Copy link
Member Author

chrahunt commented Aug 5, 2020

Hello! Do you mind creating a new issue with this question and maybe a few more details on your use case so we can provide better advice?

crazy-max added a commit to crazy-max/docker-nextcloud that referenced this issue Aug 9, 2020
jkonecny12 added a commit to jkonecny12/anaconda that referenced this issue Aug 18, 2020
This command will raise an error from pip 20.2.2.

pypa/pip#7309

Without this command we can't call coverage3 directly so we had to change our
Makefile a bit.
@nugend
Copy link

nugend commented Aug 19, 2020

pip documentation still gives "--install-scripts" as a suggested use of "--install-option"

@uranusjr
Copy link
Member

@nugend Would you mind opening a new issue for the documentation fix?

@Tyil
Copy link

Tyil commented Sep 1, 2020

@chrahunt I've been looking along this thread and haven't been able to find my answer, hopefuly you can give me an answer :)
I previously legeraged --install-option="--install-scripts=/custom/path/" to deploy only scripts to a specific location but this is now rejected with the recents updates.
Is there still a way through pip to install scripts to a custom location without moving the whole package ?

Thank you

This is used by one of our repositories at work, to keep all scripts used at $dayJob in the same location, and I haven't been able to find any way to get the behaviour with any new options yet. Any suggestions on how to get this behaviour in the new situation are much appreciated.

@uranusjr
Copy link
Member

uranusjr commented Sep 1, 2020

As stated above, please open a new issue. I think this fits well into the recent configurable install scheme discussion, but it should be discussed as a feature request on its own, not buried under a closed issue.

pingtimeout added a commit to pingtimeout/docker-graphite-grafana that referenced this issue Sep 25, 2020
See pypa/pip#7309. Using --install-option and
either of --prefix or --install-lib results in an error starting at pip
20.2.
nook24 pushed a commit to it-novum/graphing-docker that referenced this issue Apr 19, 2021
@thorntonryan
Copy link

thorntonryan commented Apr 27, 2021

@chrahunt I've been looking along this thread and haven't been able to find my answer, hopefuly you can give me an answer :)
I previously legeraged --install-option="--install-scripts=/custom/path/" to deploy only scripts to a specific location but this is now rejected with the recents updates.
Is there still a way through pip to install scripts to a custom location without moving the whole package ?
Thank you

This is used by one of our repositories at work, to keep all scripts used at $dayJob in the same location, and I haven't been able to find any way to get the behaviour with any new options yet. Any suggestions on how to get this behaviour in the new situation are much appreciated.

For anyone else who happens to run across this.

We were also were using --install-option="--install-scripts=/custom/path/" in a similar way.

We were using it to package utility scripts or other in venvs and put those entry point / console scripts in a folder that was in path, but without wanting to add a bunch of python / pip or other dependencies (e.g. futurize/pasteurize) from the target package and its dependencies to path. Using --install-scripts helped us put "some" but "not all" of the things in a custom location.

With the deprecation and pending removal, the workaround we've settled on is to split the install into two steps:

-pip install sphinx --install-option="--install-scripts=/custom/path"
+pip install sphinx
+pip install sphinx --no-deps --force-reinstall --target "/custom/path" 
  1. Installs the package and any dependency.
  2. Re-installs the package in our desired folder, but since we gave --no-deps, won't include any other dependency.

Since we're working with venvs, for our usage, this has the desired effect.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
C: cli Command line interface related things (optparse, option grouping etc) kind: backwards incompatible Would be backward incompatible project: setuptools Related to setuptools !release blocker Hold a release until this is resolved type: deprecation Related to deprecation / removal.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants