有时候由于特定的依赖关系,比如以前安装的包X要求它依赖的A包的版本必须低于2.0,会导致新的包无法正常安装或升级。比较常见的情况是当安装新包的时候,conda会选择它的一个比较旧的版本。
比如最近我就遇到了pytorch被强制锁定在1.2版本上,而无法安装最新的1.4版本。经过我查看依赖关系发现,这是以为我安装的torchvision 0.4.0版本要求pytorch只能是1.2。而torchvision只能是0.4.0版本而不是最新的0.5.0的原因是torchvision是支持cudatoolkit 10.0的最高版本。更新的版本需要cudatoolkit 10.1。而我之前安装的tensorflow 1.14要求cudatoolkit必须是10.0。为了保证之前安装的包不受新包的影响,我的cudatoolkit被锁定在了10.0,即使我在conda安装命令中写cudatoolkit=10.1
也不会被conda理会。所以解决的办法就是卸载tensorflow 1.x版本。
对于复杂一点的情况,手工发现这中依赖锁定的关系非常困难。最好使用一些自动化的依赖关系可视化工具。比如本次介绍的conda-tree。它的优势在于,它依赖少,并且可以把依赖信息直接打印在命令行里(或者很方便地重定向到文件里),而且它显示的依赖信息是包含版本要求的。
类似的依赖显示工具还有一些,比如下面这两个,它们可以绘制好看的图片。
https://gist.github.com/ericdill/9942ac55c2c9f6a550973dd2dc3653a4
https://gist.github.com/msarahan/f425082c67bd985cb4edd0edb4d50de0
conda会将所有已安装的包的信息存放在conda-meta文件夹下。每个包的信息都被存放在一个json文件中。它保存了这个包的名称、描述、来源、依赖、每个文件的安装路径等信息。通过解析这些文件,就可以构造出当前安装的所有包的依赖树。
每一个virtual environment都有一个自己的conda-meta文件夹,它保存在这个virtual environment的根目录下。默认的根环境(或者叫base环境)的根目录就是anaconda的根目录。
conda-tree工具就是自动读取它们,并且分析整理生成依赖图。
详细信息可以参考它的github主页:
https://github.com/rvalieris/conda-tree
它要求预先安装networkx包。
这个包是anaconda的一部分,所以如果当前环境是默认环境(base),或者安装了anaconda,那么不需要单独安装它。
如果当前环境是自己创建的并且不确定有没有安装networkx,可以通过下面命令来安装。
conda install networkx
使用下面的命令来安装:
conda install -c conda-forge conda-tree
其中-c conda-forge
表示在conda-forge这个channel而不是默认的官方channel搜索conda-tree这个包。conda-forge是最大的第三方channel。
安装成功之后可以使用命令:
conda-tree -h
来查看用法和帮助信息。下面这是当前版本的信息。
usage: conda-tree [-h] [-p PREFIX] [-n NAME] [-v]
{leaves,cycles,whoneeds,depends,deptree} ...
positional arguments:
{leaves,cycles,whoneeds,depends,deptree}
leaves shows leaf packages
cycles shows dependency cycles
whoneeds shows packages that depends on this package
depends shows this package dependencies
deptree shows the complete dependency tree ('python' is
excluded to avoid cycles)
optional arguments:
-h, --help show this help message and exit
-p PREFIX, --prefix PREFIX
-n NAME, --name NAME
-v, --version show program's version number and exit
它支持查看顶层包(叶子节点),查找循环,查找哪些包依赖了指定的包,查找指定的包依赖了那些包,在命令行中绘制整体依赖树。非常好的一点是它展示依赖关系都是包含版本号要求的。
具体的用法大家可以自己尝试或者在它的github主页上查看,这里我摘录一部分给大家感受一下。
# which packages depend on a specific package
$ conda-tree whoneeds xz
['samtools', 'bcftools', 'htslib', 'python']
# dependency cycles
$ conda-tree cycles
pip -> python -> pip
pip -> wheel -> python -> pip
# full dependency tree
$ conda-tree deptree --full
neovim==0.3.1
├─ pynvim 0.3.2 [required: any]
│ ├─ greenlet 0.4.15 [required: any]
│ │ └─ python 3.7.3 [required: >=3.7,<3.8.0a0]
│ │ ├─ bzip2 1.0.8 [required: >=1.0.6,<2.0a0]
│ │ ├─ libcxx 8.0.1 [required: >=4.0.1]
│ │ │ └─ libcxxabi 8.0.1 [required: 8.0.1, 0]
│ │ ├─ libffi 3.2.1 [required: >=3.2.1,<3.3.0a0]
...
conda-tree==0.0.4
├─ conda 4.7.11 [required: any]
│ ├─ conda-package-handling 1.4.1 [required: >=1.3.0]
│ │ ├─ libarchive 3.3.3 [required: >=3.3.3]
│ │ │ ├─ bzip2 1.0.8 [required: >=1.0.6,<2.0a0]
...
我个人建议的用法是使用下面命令,把依赖树输出到一个文件里,比如tree.txt
。然后用文本编辑软件打开这个文件来查看,并查找需要的条目。
conda-tree deptree > tree.txt
默认不带参数的deptree子命令只在第一次遇到某个包时绘制它的依赖树,以后再遇到时只会提示之前显示过。在加了--full
参数之后绘制完整的依赖树。