问题
Why does sys.path
contain /usr/...
before directories from my virtualenv?
I create the virtualenv with --system-site-packages
The sys.path
looks like this at the moment:
/home/my-virtualenv/src/foo
/usr/lib/python2.7/site-packages <--- /usr paths should be below
/usr/lib64/python2.7/site-packages
/home/my-virtualenv/lib/python27.zip
/home/my-virtualenv/lib64/python2.7
/home/my-virtualenv/lib64/python2.7/plat-linux2
/home/my-virtualenv/lib64/python2.7/lib-tk
/home/my-virtualenv/lib64/python2.7/lib-old
/home/my-virtualenv/lib64/python2.7/lib-dynload
/usr/lib64/python2.7
/usr/lib/python2.7
/usr/lib64/python2.7/lib-tk
/home/my-virtualenv/lib/python2.7/site-packages
I want all paths outside my virtualenv (/usr...
) to be below the paths of the virtualenv.
Otherwise crazy things happen: I install a package with pip. Pip tells me that the new version is installed (pip freeze | grep -i ...
) but the import does use the one from /usr/lib/python2.7/site-packages
I can't use --no-site-packages
in my context.
Is there a way to sort sys.path
?
Why I use system-site-packages
There seems to be no straight forward way to make single libraries from the global site-packages available in the virtualenv. See this question: make some modules from global site-packages available in virtualenv
There are packages like python-gtk which are very hard to install in a virtualenv.
回答1:
Edit, after discussion:
"I want all paths outside my virtualenv (/usr...) to be below the paths of the virtualenv. [...] It needs to be sorted."
Then, just sort your sys.path
before the first import happens. Given a certain path prefix
corresponding to the location of your virtalenv, this approach likely is sufficient:
sys.path = sorted(sys.path, key=lambda x: x.startswith(prefix), reverse=True)
sorted()
's sorting behavior is stable: the original order is preserved for items with same sort key. Here, only two sort keys are used: True
and False
. You need to come up with a reliable way to set your prefix
(You might want to hard-code it, or determine it based on the current working directory, I am sure you find a way).
Original answer (still valid, in general):
You did not want to elaborate on your requirements and application scenario too much, so I provide a more general answer: you may need to overthink your approach, and maybe not expect virtualenv
to entirely solve your problem.
In certain situations, virtualenv
is just not the perfect solution. It is a compromise, and one side of this compromise is exhaustively described in this article: https://pythonrants.wordpress.com/2013/12/06/why-i-hate-virtualenv-and-pip/
In many scenarios virtualenv
serves a splendid purpose and does its job very fine! It helped me a lot, for sure, especially for development purposes. In other scenarios, it is either not a complete solution or even a bad solution.
So, there are a few different options now that I see:
- Use virtualenv, but take control over those things that you need to have changed. For instance, modify
sys.path
from within your package before doing a certain import. While some might consider this "not clean", it for sure is a very quick way to efficiently and reliably control directory search order.sys.path.insert(1, foo)
is actually used by many packages and test environments. It not uncommon at all. This approach may provide you with a working solution after just one minute of work. Give it a try! - If you think that virtualenv in your case does not behave as documented or clearly shows buggy behavior, then please report you findings to the project. They will surely appreciate your concise feedback.
- If you think that virtualenv does not provide the level of isolation or control that you need in your application case, you might want to look at other options, such as Docker or Vagrant, or fully-features virtual machines.
回答2:
You can simply prepend desired path before module imports. Kinda hacky, but should solve your problem
import sys
sys.path.insert(1, '/home/my-virtualenv/lib/python2.7/site-packages')
import lalala
回答3:
If you're using interactive shell you can set PYTHONSTARTUP
, or also set a few bash aliases to modify your PYTHONPATH
in the ways that you prefer.
You can also modify the activate files activate.csh
, activate_this.py
in the bin
directory of your virtualenv
; or the site.py
file as another response mentioned.
pip + virtualenv
is - as it has been pointed out not a very friendly combo for system-wide packages, i.e. there is no default flag for say use system numpy or package x,y when building an environment. Although as has been suggested in comments I would also recommend building with --no-site-packages
and then importing specifically what you need from /usr/lib
回答4:
Below straight solution resolve your problem:
1- Open my-virtualenv/lib/python2.7/site.py
file for edit.
2- Append below lines of sort algorithm(or any code that resolve your problem) at the end of this file(without first line indent):
prefix = __file__.rsplit(os.sep, 3)[0]
Below code line Suggested by @Jan-Philip Gehrcke:
sys.path = sorted(sys.path, key=lambda x: x.startswith(prefix), reverse=True)
3- Run your program and enjoy
This solution helping you to sort the sys.path
list without adding annoying lines to body of your program.
Note: The site module is automatically imported during initialization. The automatic import can be suppressed using the interpreter’s -S
option.
回答5:
A easy way I found to solve this problem was just to add your virtualenv site-packages folder to the $VIRTUAL_ENV/lib/python2.7/site-packages/_virtualenv_path_extensions.pth file or just enter in shell after activate the environment:
$ add2virtualenv $VIRTUAL_ENV/lib/python2.7/site-packages
this would force to appear the folder before your systems folders as I notice it does for the others paths added.
I believe this is not the cleanest solution, but indeed virtualenv strangely order the paths.
Another observation is when I start ipython the sys.path appears in a reasonable order, but when inside the python interpreter doesn', not sure why tho.
来源:https://stackoverflow.com/questions/28239484/sorting-sys-path-first-virtualenv-then-usr