poetry icon indicating copy to clipboard operation
poetry copied to clipboard

Install globally with --user

Open raxod502 opened this issue 4 years ago • 32 comments

  • [x] I have searched the issues of this repo and believe that this is not a duplicate.
  • [x] I have searched the documentation and believe that my question is not covered.

Feature Request

I am using Poetry to install project dependencies into a Docker container where I do not have root access. Since using a virtualenv would be unnecessary (there is only ever one project in this container) and a waste of time (this is done as part of a web service aiming at speed), I configure settings.virtualenvs.create to false. However, this causes Poetry to try to install the packages system-globally, which fails because I don't have root access in the container.

I would like a way to tell Poetry to use pip install --user instead of just pip install to install packages when virtualenvs are disabled.

raxod502 avatar Jul 09 '19 22:07 raxod502

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 13 '19 12:11 stale[bot]

Closing this issue automatically because it has not had any activity since it has been marked as stale. If you think it is still relevant and should be addressed, feel free to open a new one.

stale[bot] avatar Nov 20 '19 12:11 stale[bot]

I picked up this issue and started working on an implementation

Can somebody re-open the issue please? cc: @sdispater

richin13 avatar Dec 05 '19 06:12 richin13

Hey @richin13 ,

thanks a lot for your contribution! I reopened this issue and assigned you for seeing that this is in progress.

As this is a new feature @sdispater has to decide if it gets included. But it looks like a useful thing to me :+1:

fin swimmer

finswimmer avatar Dec 05 '19 06:12 finswimmer

Awesome, thank you @finswimmer

richin13 avatar Dec 05 '19 14:12 richin13

Shouldn't the following just work?

pip install --user /path/to/poetry-project

This should result in your project being built in an isolated environment without development requirements and pip installing the built package respecting the --user flag.

abn avatar Jun 15 '20 15:06 abn

@richin13 did your submission go anywhere or was it ignored? Currently my workaround is:

pip install --user --requirement <(poetry export --format requirements.txt)

However, this doesn't give me the project itself installed into the user.

I also tried setting PIP_USER=1 hoping that it would get passed through poetry down to the underlying pip command, but no joy.

harrybiddle avatar Aug 27 '20 08:08 harrybiddle

@harrybiddle why not let pip install the project itself as suggested above?

@raxod502 @richin13 I beleive this behaviour is dependent on the underlying pip version (ie. the pip provided in your container). This often times (if ensured via python -m ensure pip) tends to be older versions.I beleive since 20.0, pip defaults to installing to user site when not root. Trying a pip install --upgrade pip prior to doing poetry install might do the trick here.

abn avatar Aug 27 '20 08:08 abn

Hey @abn, thanks for the help, I really appreciate it.

I just gave this a go and it worked, except for two things: (1) my dev dependencies didn't get installed, and (2) I think your command installs the dependencies in the pyproject.toml rather than the pinned dependencies in poetry.lock, is that correct?

I couldn't see an option to pip install to pick up dev dependencies from pyproject.toml, and I played around with its --constraint option, but could not get it to work. In the end I came up with this command (pip 20.2.2, poetry 1.0.10):

pip install --requirement <(poetry export --dev --format requirements.txt)
pip install --no-deps .

This works where poetry config virtualenvs.create false && poetry install fails on Ubuntu 18.04 (it fails uninstalling system packages).

harrybiddle avatar Aug 27 '20 10:08 harrybiddle

@raxod502 What is the reason you don't want to install the dependencies "system wide" on the container? It would just require to do the USER switch after the dependencies have been installed.

tbrlpld avatar Dec 23 '21 20:12 tbrlpld

At the time, it was because I was working with a system where the dependencies were installed at runtime (in response to incoming requests), not as part of the Dockerfile build. Therefore, any solution that required modifying the base image wasn't ideal.

I believe it would be possible to work around the problem by creating a virtualenv in the base image and hardcoding some environment variables (or initializing them as part of the ENTRYPOINT script), but these solutions just didn't seem as elegant as installing with --user, which is already supported directly by Pip.

raxod502 avatar Dec 26 '21 21:12 raxod502

Oh interesting.

tbrlpld avatar Jan 06 '22 18:01 tbrlpld

Found this here issue when looking for the equivalent of poetry install --user. The reason is that I am working in a VSCode development container, and want to avoid the unnecessary fuss of creating a virtualenv, thus poetry config virtualenv.create false. using sudo poetry install doesn't feel quite right.

It would even be better to just configure this, somehow, so that my development container could have these settings here:

poetry config virtualenv.create false
poetry config install.user true

or similar.

It's perfectly possible to create a virtualenv inside the development container, but this is really quite redundant, because in most cases, there is only ever the single environment inside it. This saves a bunch of steps and indirections.

kristjanvalur avatar Sep 07 '22 16:09 kristjanvalur

Please see #6398 -- virtual environments are not really a layer of 'fuss', but the way you keep your Python peas and potatoes from touching. They're closer to a node_modules folder or similar in the Python world.

I'm mostly of the opinion that installing to site.getuserbase() is basically the same as a virtual environment, but with site-packages leaking in. The only advantage is not having to prefix commands in the container with the virtual environment, but there are plenty of other patterns that avoid that (see https://github.com/python-poetry/poetry/issues/6397#issuecomment-1236327500 for instance).

This is difficult/unlikely to be supported in the medium term as we are planning to migrate from pip to performing installs ourselves -- and thus PIP_USER and similar will not work with the new installer. It's possible we could support this in the new installer, but I find that this might be useful for completeness (there are situations where virtualenvs.create false is probably what you do want, but they are rare), but it's mostly a footgun that few people will find real value in (mostly, it will cause them headaches).

neersighted avatar Sep 07 '22 16:09 neersighted

Thanks for your comment. When working with containers, turning off virtualenvs is sometimes definitely what one wants. there are no separate peas and potatoes, I control all python packages installed. For production containers, making them as tight as possible is sometimes preferable, hence instaling without virtualenv is what one regularly does. This is also not a problem when building containers, because one can simply use the proper UID when doing so. It is slightly less convenient when working in a development container because then one currently has to sudo.

Containers can provide exactly the same kind of application isolation as virtualenvs do, and so using one within the other is often quite superfluous.

Of course, Poetry is great with virtualenvs and dependency management and thus our preferred tool. People on mac/linux, for example, won't use the dev container, but just the virtualenv. People on Windows may elect to use a dev container, because that gives them a linux environment. And when building production containers, we install directly into the root.

No big deal, just wanted to throw this out there :)

kristjanvalur avatar Sep 09 '22 11:09 kristjanvalur

I'm still asking the question of why -- you don't control every Python package in the root, as you are subject to packages that come from the operating system. If your base image does something odd, you will be exposed to packages outside your control in the system prefix. This is not a hypothetical -- Ubuntu ships Python code in dist-packages with version numbers incompatible with standard tooling, causing Poetry to choke on their version numbers.

It's not superfluous to isolate Poetry from your code, and your code from the operating system -- you of course are free to do what you want, but it's an incredible burden to try to support every quirky patch a linux distribution ships, when a virtual environment prevents 90% of their upstream-incompatible changes to Python from affecting your project. I have to stress that if you give up letting Poetry manage your environment exclusively (by having Poetry operate in a virtual environment that only it installs packages to), you do so at your own risk and with breakage being likely.

neersighted avatar Sep 09 '22 14:09 neersighted

I've been a core developer of CPython since long before virtualenvs were invented and so I´m well aware of their utility. But as I say, containers can solve the isolation problem just as well. A typical deployment container's baseline is very bare bones and comes without any of the baggage of a distro. A Python container contains no python components other than the standard lib.

As a developer, I elect to install my locked dependencies in to the root of the deployment container, because there is nothing to isolate them from, and there is no virtualenv to initialize at runtime. And as explained, this already is trivial to do, by disabling virtualenv generation for Poetry and using the uid of root, as one does during Dockerfile execution.

As a developer, I also control the contents of my development container. It also is very slim and has no distro baggage. I'd like the simplicity of installing my locked dependencies directly and not into a virtualenv. It is a safety belt, a tool that I might elect not to use, as an adult, in the safety of my own dev container.

But allow me to reiterate, it is no fuss. I was simply assuming that it was possible and looking for the knob to turn, because already, the option is in place not create a virtualenv, and so it looks like an obvious extension: If not using a virtualenv, install into the user folder rather than the root. Poetry is an awesome tool even if it doesn't respond to every whim of a cranky old developer. Cheers!

kristjanvalur avatar Sep 10 '22 10:09 kristjanvalur

It's technically been there on and off (as we have used pip to implement installation -- in 1.2 I don't think just setting PIP_USER will work however), but we plan to migrate to build + installer longer-term and thus will be unlikely to re-implement --user ourselves unless there is a very compelling reason.

neersighted avatar Sep 10 '22 10:09 neersighted

Right. Didn't realize there was anything to implement, as such, since the user folder has been part of Python since before importlib (IIRC). And also, didn't know that it was even possible to go without pip. But never mind, Python packaging has always been its own incredibly mysterious and complex part of the ecosystem, one that I have steered (mostly) clear of. Keep up the good work!

kristjanvalur avatar Sep 10 '22 10:09 kristjanvalur

I'm still asking the question of why -- you don't control every Python package in the root, as you are subject to packages that come from the operating system. If your base image does something odd, you will be exposed to packages outside your control in the system prefix. This is not a hypothetical -- Ubuntu ships Python code in dist-packages with version numbers incompatible with standard tooling, causing Poetry to choke on their version numbers.

It's not superfluous to isolate Poetry from your code, and your code from the operating system -- you of course are free to do what you want, but it's an incredible burden to try to support every quirky patch a linux distribution ships, when a virtual environment prevents 90% of their upstream-incompatible changes to Python from affecting your project. I have to stress that if you give up letting Poetry manage your environment exclusively (by having Poetry operate in a virtual environment that only it installs packages to), you do so at your own risk and with breakage being likely.

I came to this bug looking for a solution for the following scenario. Although as I'll get to, it turns out I think we can actually get away without --user in my case.

We create Docker images of our Python websites to push to Kubernetes. We naturally want these images to be as lean as possible so deployment and any dynamic replacement done by Kubernetes itself are as quick as possible. We'd also like them to be as straightforwardly implemented as possible so they're easier to reason about and fix. Nonetheless, it would be a fantastic improvement for us to be able to benefit from Poetry's lockfile functionality.

I accept that using a Python virtualenv isn't much of an overhead. But in this case is should genuinely be unnecessary, as we start from a very stripped down base-layer version of Ubuntu, which doesn't even have Python installed, and then we explicitly choose what goes in.

We currently use pip install --user in a build step within our Dockerfile, so that we can encapsulate all the site's dependencies in /root/.local/lib/ for copying into our final image. So we were hoping to simply do poetry install --user to replicate this same functionality.

However it turns out that we don't really need to do that because in fact the actual dpkg system modules get installed into /usr/lib/, whereas poetry install (with virtualenvs.create false) will install them into /usr/local/lib/. Hence in our case we can simply use (in this case) /usr/local/lib/python3.10/dist-packages/ to encapsulate our dependencies.

nottrobin avatar Sep 30 '22 09:09 nottrobin

Yes. When building deployment images, this is exactly that which we do ourselves. It's easy because at build-time we are UID==0. My use case was more about development time, when you have a similarly clean container to work in, but you don't want to be root when installing dependencies, and may want to skip the step of activating a virtualenv. No biggie, but would be nice :)

kristjanvalur avatar Sep 30 '22 11:09 kristjanvalur

I think it would be useful to reconsider this issue in order to enhance the relationship between poetry and devcontainers as stated by https://github.com/python-poetry/poetry/issues/1214#issuecomment-1239587131. :pray:

Matesanz avatar Jan 19 '23 14:01 Matesanz

There is another use case where this feature would be useful: extending a pre-existing image where pip install was done to the user location. An example is adding custom code to the stock airflow image.

sryabkov avatar Jan 19 '23 15:01 sryabkov

I think it would be useful to reconsider this issue in order to enhance the relationship between poetry and devcontainers as stated by #1214 (comment). 🙏

I landed in this issue for this exact reason, most of my devcontainers I run as a non-privileged user, but for poetry managed projects I'm finding I need to run as root so I don't need to jump through a bunch of hoops just to run poetry install.

DougPlumley avatar Feb 10 '23 13:02 DougPlumley

For a devcontainer you could pre-create the virtual environment that will be used. I.e.:

ENV VIRTUAL_ENV="/opt/venv"
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
RUN python3 -m venv $VIRTUAL_ENV && \
  # Not strictly required, just to set PS1 and a deactivate function for root and users created after this command
  echo ". ${VIRTUAL_ENV}/bin/activate" >> /etc/bash.bashrc && \
  echo ". ${VIRTUAL_ENV}/bin/activate" >> /etc/skel/.bashrc

And ensure that your user in the container has write access to $VIRTUAL_ENV. Poetry commands after this will use the venv

hberntsen avatar Feb 10 '23 14:02 hberntsen

Seconded, this would be a nice feature to have, I also want to be able to extend an existing pre-built python installation at runtime so that i do not have to reinstall all 400+ packages into a new virtualenv.

fjmacagno avatar May 26 '23 19:05 fjmacagno

My use-case is a bit more mundane. I just want to have a global poetry.lock managed installation for throwaway scripts or a REPL for rough calculations/experimentation which is not associated with a specific project.


Using poetry to manage --user site-packages

Create a directory for your poetry project:

mkdir -p ~/.config/pypoetry/envs/default
cd ~/.config/pypoetry/envs/default
poetry init

Symlink the resulting virtualenv, prepend to PATH, and set up a convenient alias:

PYVER=3.11

rm -rf ~/.local/lib/python$PYVER
ln -s ~/.cache/pypoetry/virtualenvs/default-*-py$PYVER/lib/python$PYVER ~/.local/lib/python$PYVER
ln -s ~/.cache/pypoetry/virtualenvs/default-*-py$PYVER/bin ~/.local/bin_python

echo 'export PATH="$HOME/.local/bin_python:$PATH"' >> ~/.bashrc
echo "alias poetry-user='poetry --directory ~/.config/pypoetry/envs/default'" >> ~/.bashrc

And now, instead of poetry --user, just do poetry-user:

poetry-user add pyyaml

YodaEmbedding avatar Aug 07 '23 07:08 YodaEmbedding

I think I've found a workaround:

just create a venv in same location as user's .local

RUN python3 -m venv $HOME/.local \
&& source $HOME/.local/bin/activate \
&& poetry install

EgorBlagov avatar Nov 24 '23 16:11 EgorBlagov

@sryabkov @DougPlumley @fjmacagno out of curiosity, is there a reason why https://python-poetry.org/docs/configuration/#virtualenvsoptionssystem-site-packages does not work for your scenario? In this case, poetry will only install the packages that it needs to override or if the version available in the system environment does not satisfy the locked versions. And that is the reason why this option was added in the first place. If you have a container example I can tinker with to understand the issue better - airflow or devcontainer scenario, I would maybe offer specific resolutions or better understand how we can improve the poetry features.

And as for when running as an unprivileged user, poetry should not (when using the new installer) try to install to the root site. I will detect the user site (or the venv site) and install it there. This is determined by the property below.

https://github.com/python-poetry/poetry/blob/cff4d7d51c9ac0f9a8b9ac91652b6dc20e63e203/src/poetry/utils/env/site_packages.py#L61-L71

abn avatar Feb 23 '24 13:02 abn

@sryabkov @DougPlumley @fjmacagno out of curiosity, is there a reason why https://python-poetry.org/docs/configuration/#virtualenvsoptionssystem-site-packages does not work for your scenario? In this case, poetry will only install the packages that it needs to override or if the version available in the system environment does not satisfy the locked versions. And that is the reason why this option was added in the first place. If you have a container example I can tinker with to understand the issue better - airflow or devcontainer scenario, I would maybe offer specific resolutions or better understand how we can improve the poetry features.

And as for when running as an unprivileged user, poetry should not (when using the new installer) try to install to the root site. I will detect the user site (or the venv site) and install it there. This is determined by the property below.

https://github.com/python-poetry/poetry/blob/cff4d7d51c9ac0f9a8b9ac91652b6dc20e63e203/src/poetry/utils/env/site_packages.py#L61-L71

Hi @abn, it might?

I took an example project where I ran into the permission issues and trimmed it down to this https://github.com/DougPlumley/poetry-devcontainer. Hopefully that provides some context.

If I recall correctly the reason I was using poetry config virtualenvs.create false was because VS Code/Pylance was not gracefully handling which Python virtualenv to use, so static code analysis wasn't happening as expected and running code selections wasn't happening as expected.

It could be there is an easy workaround that would have solved that, I used poetry config virtualenvs.create false and opted to just run as root for the development environment.

DougPlumley avatar Feb 23 '24 18:02 DougPlumley