混合 Python virtualenv 包和发行包?

Mix Python virtualenv packages with distro packages?

当您使用 Python virtualenv 但您希望通过发行版的包管理器安装一些包时,有什么好的处理方法?

假设您需要 lxml,但因为您无法获得 pip install lxml 来处理 Ubuntu。你真的不想在这上面浪费时间,所以你只需要 apt-get install python-lxml.

现在,您可以使用 --system-site-packages 创建一个 virtaulenv,并且现在可以访问系统范围内安装的预编译 lxml。但是您还将拖入不需要的所有其他系统范围的包!是的,将有相当多的软件包将通过 sudo pip ...sudo apt-get python-... 安装在 virtualenv 之外,因此没有 "just keep the system clean and install everything you can in virtualenvs, so that --system-site-packages won't drag too many packages with it"不是我这里的解决方案。

那么,有没有办法只安装一些特定的系统站点包?

我总是使用 pip install --user packagename – 它不需要 sudo。

如果您还没有 pip,或者 OS 的 pip 或 OS 的 easy_install 无法正常工作,首先在用户的主目录中安装setuptools,然后将其easy_install用于pip

$ wget https://bootstrap.pypa.io/ez_setup.py -O - | python3 - --user
$ easy_install-3.4 --user pip

另一方面,如果您决定在 virtualenv 中使用 pip,请不要使用 --user 选项,原因很明显。


按照您描述的方式进行操作

例如,我想导入 OS 中的模块 curl

$ pip3 install --user virtualenv
$ python3 -m virtualenv myvenv
$ cd myvenv
$ source bin/activate
(myvenv)$ python3
>>> import curl
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named curl

virtualenv 中没有这样的模块。只是为了确保我查看了 sys.path.

中的内容
>>> import sys
>>> sys.path
['',
'/home/username/myvenv/lib/python27.zip',
'/home/username/myvenv/lib/python2.7',
'/home/username/myvenv/lib/python2.7/lib-dynload',
'/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2',
'/home/username/myvenv/lib/python2.7/site-packages']

请注意,没有 /usr/lib/python3.4/site-packages,如您所愿,但存在 /usr/lib/python3.4。如果这对您来说不合适,请在创建 virtualenv 时使用 --always-copy 选项。

在接下来的步骤中,我创建了指向 curl 模块及其依赖项的符号链接。

>>> ^D # Press Control-D
(myvenv)$ ln -s /usr/lib/python3.4/site-packages/curl lib/python3.4/site-packages/

注意第二个参数(符号链接指向的文件或目录)中没有尾部斜杠,但第三个参数(应该创建符号链接的地方)有一个斜杠。那是因为我在目录 lib/python3.4/site-packages 的 中创建了一个符号链接 。如果没有尾部斜杠,它将尝试替换目录。

让我们检查一下是否有效。

(myvenv)$ python3
>>> import curl
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/username/myvenv/lib/python3.4/site-packages/curl/__init__.py", line 9, in <module>
    import sys, pycurl
ImportError: No module named 'pycurl'

没有。 curl 取决于 pycurl。我们需要更深入。

>>> ^D
(myvenv)$ ls -1 /usr/lib/python3.4/site-packages/pycurl*
/usr/lib/python3.4/site-packages/pycurl-7.19.3.1-py3.4.egg-info
/usr/lib/python3.4/site-packages/pycurl.cpython-34m.so
(myvenv)$ ln -s /usr/lib/python3.4/site-packages/pycurl-7.19.3.1-py3.4.egg-info lib/python3.4/site-packages/
(myvenv)$ ln -s /usr/lib/python3.4/site-packages/pycurl.cpython-34m.so lib/python3.4/site-packages/
(myvenv)$ python3
>>> import curl
>>> curl
<module 'curl' from '/home/username/myvenv/lib/python3.4/site-packages/curl/__init__.py'>

终于。

这是你需要的吗?


就个人而言,我强烈建议您不要这样做,因为首先,这是一种变态。

此外,我怀疑这种方法是否适用于所有情况。依赖的深度可能很可怕。

其次,我认为没有理由避免 --system-site-packages 它在我所承认的所有情况下都非常有效。

pip 安装在用户主目录中的包比系统范围的包具有更高的优先级,因此,在执行 import 时它们首先被采用。 在 virtualenv 中,只有在使用 --system-site-packages 选项创建 virtualenv 时才会导入它们。 pip 在 vi​​rtualenv 中安装的包具有更高的优先级。

老实说,我写这个答案只是想问,为什么 你想要搞砸所有这些而不是让 pip 工作。我还不能写关于 SO 的评论,所以这就是为什么我写了一个完整的答案。 :)

But you'll also drag in all the other system wide packages that you don't need!

不,你不会。只有在执行 import 时才将模块加载到内存中。如果你的意思是这个包将出现在 virtualenv 中——再一次,它不会。如果你看过 myvenv/lib/python3.4/site-packages/ 的内部,你会发现里面只有两个包 - pipsetuptools(尽管后者被分成几个目录)。 virtualenv 中的 Python 解释器就像普通解释器一样简单地加载系统范围的模块。


P.S。上面的代码也适用于Python 2.7。只需将 pip3 替换为 pip2,将 python3 替换为 python2,将 3.4 替换为 2.7,等等

P.P.S。您可以将模块及其依赖项复制到 virtualenv 到相应的目录,而不是符号链接。这在使用 --always-copy 时很有意义,因此您的 virtualenv 变得可移植。

P.P.P.S。如果您决定将 virtualenv 与 --system-site-packagespip 一起使用,并且假设您希望 requests 软件包比 /usr/lib 中已安装的软件包更新,请使用 pip install -I,如所述 here