improve reliability of `poetry shell` configuring $PATH properly
See original GitHub issue- I am on the latest Poetry version.
- I have searched the issues of this repo and believe that this is not a duplicate.
- If an exception occurs when executing a command, I executed it again in debug mode (
-vvv
option).
- OS version and name: macOS 10.14
- Poetry version: 0.11.5
Issue
From https://github.com/sdispater/poetry/issues/198#issuecomment-430742299.
Preface: I’m not sure if this can be realistically solved by Poetry or if you’re too much at the mercy of each person’s shell configuration. But here goes.
Certain shell configurations interact poorly with poetry shell
; see https://github.com/sdispater/poetry/issues/198#issuecomment-424016403 and https://github.com/sdispater/poetry/issues/172 for examples. Resolutions to the issue tend to be ad-hoc workarounds that involve spelunking in your shell configuration, see https://github.com/sdispater/poetry/issues/198#issuecomment-430742299 and https://github.com/sdispater/poetry/issues/172#issuecomment-401554154 respectively.
The root of the issue seems to be that Poetry attaches the virtualenv to the beginning of $PATH
, then spawns the shell, which may override that configuration by attaching more things to the beginning of $PATH
. If possible, modifying $PATH
after the shell initializes ought to give a better result that will work by default on more systems. I don’t recall seeing this issue with pipenv shell
, but admittedly I didn’t dig as deep while I was using it as my primary tool.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:20
- Comments:9 (2 by maintainers)
Top GitHub Comments
I was curious about the inconsistency of this command so I followed the bread crumbs, the OP points the right reason, but here is my walkthrough, which may be useful:
TL;DR
don’t use
subprocess.call('/bin/bash')
AFTER setting the venv variables, it will in fact inherit the environ but the child will run the rc files after, which may or may not overshadow relevant variables, depending on the user configurationinstead, do:
This can be done via
Popen
, but a reliable and portable way is usingpexpect.spawn.sendline
followed bypexpect.spawn.interact
Explanation
1. Chasing the source lines
1 -
poetry shell
callspoetry.console.main
which callsApplication().run()
, which uses cleo’sBaseApplication
and wtv and finally gets to poetry.console.commands.shell.ShellCommand2 -
ShellCommand.handle
runsself.env.execute(Shell.get().path)
Shell.get().path
is meant to return the path of the current-running shell binary, probably no problem there3 -
self.env.execute
is the next stepself.env
is created viaEnv.create_venv(self.poetry.file.parent, o, self.poetry.package.name)
which createsVirtualEnv
which finally contains theexecute
method that is run:You can see it sets enviroment variables and calls
super().execute
(which isEnv.execute
defined in the same file)and there is the problem
2. Whats wrong?
The problem is that the environment variables are set before the shell process is created, which do inherit these variables, but can overshadow in its
.rc
files execution startup-routine. This will be the case in every shell, maybe you’re lucky and your rc files does not overshadows the relevant variables, or maybe you can work around in your rc files to guard a$POETRY_SHELL
conditional but that will always be unreliableHere’s how it works:
Now notices what happens if I spawn a new
bash
:As any child process do, all environment variables are inherited, even my PROMPT (via $PS1) is still marked as in the virtualenv the result will be exactly the same if I had called bash via python’s subprocess.call:
In both cases, the rc files will be executed as they normally do every time a
bash|zsh|wtv
process is initiaded, therefore, overshadowing the parent-inherited environment variables:Note that my prompt still inherited the parent $PS1, as I don’t have any PS1 setting in my
.bashrc
, but I do have pyenv’s shims activation, which overshadows the venv binaries3. Solution
In short, the core problem can be reproduced in a fresh shell:
Ie, 1 - “run the source command”, 2 - “start interactive shell”,
What should be done is sending the command after the startup routines:
Note the resulted lines, it wasn’t me who typed the line
$ source /home/...
it came from stdin, then the control came back to me viaexec </dev/tty
Turns out this is the same thing
pipenv shell
doesthe relevant lines are:
Ie, 1 - Create a interactive shell; 2 - queue the source command to be executed via stdin; 3 - bring subprocess interaction to user
Using pipenv aproach, this probably would do as an almost drop in replacement for the
subprocess.call
:Then just change
subprocess.call([bin] + list(args), **kwargs)
tospawn_venv(bin, self._path, args, kwargs)
This is not good enough for a PR because:
you may want to set the
dimensions
argument in thespawn
call and add a event handler for terminal resizing, pipenv uses a backport ofshutil.get_terminal_size
for thatpipenv provides a cli flag that end’s up using subprocess.call normally
I don’t know which
**kwargs
can be used, would need to normalize between subprocess.call and pexpect.spawnMaybe it breaks the simmetry between Env.execute and Env.run