sys.path will use user site-packages over the environment's site-packages
Opened this issue · 13 comments
Hello,
I'm following up on a recommendation from @jakirkham in the R-base feedstock in this issue:
conda-forge/r-base-feedstock#37 where we see similar behavior in R
with respect to .libPaths()
.
According to PEP 370 there are platform-specific paths in which users can install packages which will be included in sys.path
. The unfortunate result is that if you have a package installed there, it may be incompatible with a given conda environment's binaries and cause issues. Worse, the user-package is always preferred in import-order, so this will impact every environment (including root).
I'm kind of torn as to whether this is or is not a bug, but I'm reporting it in any case for further discussion.
Here is a demonstration showing this in a root environment from defaults and a conda-forge environment using the Python from this feedstock.
boring conda-info for completeness
16:33:55 ~
§ conda info
active environment : None
user config file : /home/evan/.condarc
populated config files :
conda version : 4.4.11
conda-build version : 3.0.28
python version : 3.6.1.final.0
base environment : /opt/Miniconda3 (read only)
channel URLs : https://repo.continuum.io/pkgs/main/linux-64
https://repo.continuum.io/pkgs/main/noarch
https://repo.continuum.io/pkgs/free/linux-64
https://repo.continuum.io/pkgs/free/noarch
https://repo.continuum.io/pkgs/r/linux-64
https://repo.continuum.io/pkgs/r/noarch
https://repo.continuum.io/pkgs/pro/linux-64
https://repo.continuum.io/pkgs/pro/noarch
package cache : /opt/Miniconda3/pkgs
/home/evan/.conda/pkgs
envs directories : /home/evan/.conda/envs
/opt/Miniconda3/envs
platform : linux-64
user-agent : conda/4.4.11 requests/2.14.2 CPython/3.6.1 Linux/4.15.7-1-ARCH arch/ glibc/2.26
UID:GID : 1000:100
netrc file : None
offline mode : False
Set up the user-site packages and add a Hello World package:
16:33:59 ~
§ mkdir -p ~/.local/lib/python3.6/site-packages
16:34:07 ~
§ echo "print('Hello World')" > ~/.local/lib/python3.6/site-packages/hello.py
Try it in the root environment:
16:34:13 ~
§ which python
/opt/Miniconda3/bin/python
16:34:20 ~
§ python
Python 3.6.1 |Continuum Analytics, Inc.| (default, May 11 2017, 13:09:58)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/opt/Miniconda3/lib/python36.zip', '/opt/Miniconda3/lib/python3.6', '/opt/Miniconda3/lib/python3.6/lib-dynload', '/home/evan/.local/lib/python3.6/site-packages', '/opt/Miniconda3/lib/python3.6/site-packages']
>>>
>>> import hello
Hello World
>>>
Note the end of sys.path: '/home/evan/.local/lib/python3.6/site-packages', '/opt/Miniconda3/lib/python3.6/site-packages']
the .local
site-packages takes precedence over the environment's.
Try it in a fresh conda-forge environment:
16:34:40 ~
§ conda create -n example -c conda-forge python=3.6
Boring output of install plan
Solving environment: done
## Package Plan ##
environment location: /home/evan/.conda/envs/example
added / updated specs:
- python=3.6
The following packages will be downloaded:
package | build
---------------------------|-----------------
certifi-2018.1.18 | py36_0 143 KB conda-forge
setuptools-38.5.2 | py36_0 525 KB conda-forge
------------------------------------------------------------
Total: 668 KB
The following NEW packages will be INSTALLED:
ca-certificates: 2018.1.18-0 conda-forge
certifi: 2018.1.18-py36_0 conda-forge
ncurses: 5.9-10 conda-forge
openssl: 1.0.2n-0 conda-forge
pip: 9.0.1-py36_1 conda-forge
python: 3.6.4-0 conda-forge
readline: 7.0-0 conda-forge
setuptools: 38.5.2-py36_0 conda-forge
sqlite: 3.20.1-2 conda-forge
tk: 8.6.7-0 conda-forge
wheel: 0.30.0-py36_2 conda-forge
xz: 5.2.3-0 conda-forge
zlib: 1.2.11-0 conda-forge
Proceed ([y]/n)? y
Downloading and Extracting Packages
certifi 2018.1.18: ##################################################### | 100%
setuptools 38.5.2: ##################################################### | 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
#
# To activate this environment, use:
# > source activate example
#
# To deactivate an active environment, use:
# > source deactivate
#
Run the same test:
16:35:21 ~
§ source activate example
(example) 16:35:27 ~
§ which python
/home/evan/.conda/envs/example/bin/python
(example) 16:35:29 ~
§ python
Python 3.6.4 | packaged by conda-forge | (default, Dec 23 2017, 16:31:06)
[GCC 4.8.2 20140120 (Red Hat 4.8.2-15)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/home/evan/.conda/envs/example/lib/python36.zip', '/home/evan/.conda/envs/example/lib/python3.6', '/home/evan/.conda/envs/example/lib/python3.6/lib-dynload', '/home/evan/.local/lib/python3.6/site-packages', '/home/evan/.conda/envs/example/lib/python3.6/site-packages']
>>>
>>> import hello
Hello World
>>>
Same behavior for .local
site-packages as in root.
I believe this behavior is true for most (all?) Python installs, including those provides by various linux distributions, macPorts, brew, etc. Since the search path order is specified in PEP 370 "The user site directory is added before the system site directories but after Python's search paths and PYTHONPATH", I think it would be unwise to change this behavior.
This can cause problems if incompatible packages are installed into the user site-packages directory, but it can also be used as a feature to provide packages which are shared by all Python installs.
What if we added some kind of warning (maybe at startup) to point out .local
is on the path? While I understand the rationale for the PEP, it does feel a little strange in the context of conda
where users can easily create their own environments anywhere. So the value of looking in .local
is at least significantly reduced if not problematic/at odds with typical use cases.
Does virtualenv do anything to disable .local
?
Great question. Not sure.
So Christian Heimes, author PEP 370, is proposing it be deprecated.
Based on that conversation, it does sounds like pip
is moving more towards using user-installs as a default. That could potentially put Python in a similar situation as R is today (where user-installs are very commonplace).
Yeah, it does sound like .local
is here to stay.
If you have time, @ebolyen, I'd suggest researching @jjhelmus' question above. Namely what does virtualenv do with .local
? Does it disable it? Does it make itself higher priority? Does it do nothing?
Once we have answers to these questions, we can see whether that solution would apply for us.
It looks like venv
disables it:
11:22:02 ~
§ python3 -m venv --without-pip venv
11:22:17 ~
§ source venv/bin/activate
(venv) 11:23:05 ~
§ python
Python 3.6.1 |Continuum Analytics, Inc.| (default, May 11 2017, 13:09:58)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/opt/Miniconda3/lib/python36.zip', '/opt/Miniconda3/lib/python3.6', '/opt/Miniconda3/lib/python3.6/lib-dynload', '/home/evan/venv/lib/python3.6/site-packages']
>>>
(venv) 11:23:20 ~
§ ls ~/.local/lib/python3.6/site-packages/
hello.py __pycache__
This seems to also be corroborated in this (kind of related) issue about the --user flag of pip with venv: pypa/pip#4141
Is this documented behavior?
Not yet: pypa/virtualenv#1005?
But it does seem to be the running behavior for a while: minor note in changelog, related PR: pypa/virtualenv#803
Yeah, turns out I've been looking in the wrong repo. But good news! There's actually a PEP that defines this behavior:
PEP 405 specifically calls out how it interacts with PEP 370 in this section.
At this point it does seem like conda should behave (at least by default) the same way as Python's builtin venv module.
I found that in conda/conda#448 this issue was discussed in 2014. The decision at that time was that conda should not exclude .local
. One helpful comment in that thread was that setting export PYTHONNOUSERSITE=True
will ignore the local user site directory.
It may be worth re-opening that discussion if there is interest.
For reference, section of PEP 405 pointed out by @ebolyen above:
PEP 370 user-level site-packages are considered part of the system site-packages for venv purposes: they are not available from an isolated venv, but are available from an
include-system-site-packages = true
venv.