"WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41" 在 "conda install" 期间

"WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41" during "conda install"

从今天开始我得到了很多

WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41

当我尝试使用 conda installconda update 更新或安装软件包时出现警告。例如:

(...) C:\Users\...> conda install numba
Fetching package metadata ...........
Solving package specifications: .

Package plan for installation in environment C:\...:

The following packages will be DOWNGRADED due to dependency conflicts:

    numba: 0.30.0-np111py35_0 --> 0.30.1-np111py35_0

Proceed ([y]/n)? y

numba-0.30.0-np111p35_0 100% |###############################| Time: 0:00:00   2.50 MB/s
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41
WARNING conda.gateways.disk:exp_backoff_fn(47): Uncaught backoff with errno 41

之后安装了软件包,但所有这些警告似乎表明某些地方无法正常工作。

OS: Windows 10 64 bit
conda: 4.3.4

你能告诉我需要做什么来修复这些警告吗?或者我可以忽略它们吗?

根据当前的 conda 源代码树,您看到的这些警告是 "normal"。 要了解上述警告的来源,让我们看一下问题中的源代码和最近在 conda 存储库 (https://github.com/conda/conda) 中提交的代码。 打印您看到的警告的相关源代码如下:

https://github.com/conda/conda/blob/4.3.4/conda/gateways/disk/init.py

# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

import sys
from errno import EACCES, ENOENT, EPERM, EPROTOTYPE
from logging import getLogger
from os.path import basename
from time import sleep

from ...common.compat import on_win

log = getLogger(__name__)

MAX_TRIES = 7


def exp_backoff_fn(fn, *args, **kwargs):
    """Mostly for retrying file operations that fail on Windows due to virus scanners"""
    max_tries = kwargs.pop('max_tries', MAX_TRIES)
    if not on_win:
        return fn(*args, **kwargs)

    import random
    # with max_tries = 6, max total time ~= 3.2 sec
    # with max_tries = 7, max total time ~= 6.5 sec
    for n in range(max_tries):
        try:
            result = fn(*args, **kwargs)
        except (OSError, IOError) as e:
            log.trace(repr(e))
            if e.errno in (EPERM, EACCES):
                if n == max_tries-1:
                    raise
                sleep_time = ((2 ** n) + random.random()) * 0.1
                caller_frame = sys._getframe(1)
                log.trace("retrying %s/%s %s() in %g sec",
                          basename(caller_frame.f_code.co_filename),
                          caller_frame.f_lineno,
                          fn.__name__,
                          sleep_time)
                sleep(sleep_time)
            elif e.errno in (ENOENT, EPROTOTYPE):
                # errno.ENOENT File not found error / No such file or directory
                # errno.EPROTOTYPE OSError(41, 'The directory is not empty')
                raise
            else:
                log.warn("Uncaught backoff with errno %d", e.errno)
                raise
        else:
            return result

从上面的源代码来看,它似乎是在 Windows 上抛出的警告,可能会在

时出现

retrying file operations that fail on Windows due to virus scanners

使用https://msdn.microsoft.com/en-us/library/t3ayayh1.aspx进入细节,似乎errno 41对应于

ENOTEMPTY: Directory not empty

表示指定的目录不为空。 这是一个未捕获的错误,因为他们没有处理这种错误的分支(ENOTEMPTY),而例如他们有另一个错误,如 EPERMEACCES.
在提交 https://github.com/conda/conda/commit/fb2a783d9b9371559b5ea82aaf8ae631c2ce0450#diff-3757ed9862260ae3b54768b3e482e3fe
他们明确删除报告 EPROTOTYPEOSError(41, 'The directory is not empty'),所以现在您会在

中看到报告为警告的错误编号
log.warn("Uncaught backoff with errno %d", e.errno)

他们修改的另一部分是 https://github.com/conda/conda/blob/fb2a783d9b9371559b5ea82aaf8ae631c2ce0450/conda/gateways/disk/delete.py,在 delete_trash() 函数中,所以现在,如果您启用信息日志,很可能您会看到像

"Unable to fully clean trash directory %s\nThere are %d remaining file(s)."

启用
log.info("Unable to fully clean trash directory %s\nThere are %d remaining file(s).",
                 trash_dir, len(files_remaining))  

现在,delete_trash() 由您引用的两个命令(安装、更新)调用:
https://github.com/conda/conda/blob/f4b386476307e3979679957292d4f6e4c581df03/conda/cli/main_install.py
https://github.com/conda/conda/blob/a26b1eff17dcaf12f03aea5bbe8dee1e01308de7/conda/cli/main_update.py

可以看出,delete_trash()分别在前面提到的'install'和'update'文件中触发,代码片段如下:

from ..gateways.disk.delete import delete_trash

# some other code ...

def execute(args, parser):
    install(args, parser, 'install')
    delete_trash()

from ..gateways.disk.delete import delete_trash

# some other code ...

def execute(args, parser):
    install(args, parser, 'update')
    delete_trash()  

delete_trash() 然后将通过 backoff_rmdir()backoff_unlink() 触发该代码路径,最终将导致您从 exp_backoff_fn() 看到的警告,如前所述。
因此,总而言之,主要的调用链是

update or install --> delete_trash() --> backoff_rmdir() or backoff_unlink() --> exp_backoff_fn() --> your warning message

根据在存储库中所做的源代码修改,开发人员认为这些警告您可以安全地忽略,因为这些警告发生在更新或安装命令的 "cleaning" 阶段,即之后已成功执行更新或安装操作。
我不能说 100% 您是否可以安全地忽略这些警告。如果在几次尝试之后删除垃圾目录的命令成功,那么就没有问题。但是如果不成功,那么你会遇到这个目录会因为不删除它而变得越来越大的问题。回购协议中为此打开的问题很少,我不知道修复是否涵盖了您点击的代码路径。我的印象是可能不会。要获得更多见解,您可以激活信息日志级别。


更新:这个问题 https://github.com/conda/conda/issues/4164 完全提到了您报告的警告,因为由于所有重试,人们的更新和安装时间很长。正如我提到的,在所有重试(指数 back-offs)之后,删除操作可能成功或失败,该人在他的报告中也提到了这方面。
正如你在这里看到的
https://github.com/conda/conda/issues/3664
人们很少使用 hack 来解决由于重试而导致等待时间长的问题,并且还会使命令的下一个 运行 conda install Xconda update X 没有警告。这些建议是:

  1. (为了加快 retries/delete 的时间)在 conda/gateways/disk/init.py
  2. 的副本中设置 MAX_TRIES = 1
  3. 在 运行 下一个 conda install Xconda update X 之前删除 .trash 目录。请参阅 https://github.com/conda/conda/issues/3664 了解一些人使用简单脚本自动删除该目录的变通方法。这通常应该是安全的。

所以你的问题的答案是:
1) 您可以使用 https://github.com/conda/conda/issues/3664 中提到的解决方法,它使用以下 powershell 脚本(和另一个脚本):

$cir = conda info --root
$trash_dir = "$($cir)\pkgs\.trash"
if (Test-Path $trash_dir){
    Remove-Item -Recurse -Force $trash_dir
}
conda --debug update --all --yes --quiet 

基本上清理 .trash 目录;

2) 您可以安全地忽略这些警告,因为它们不会影响功能;问题是填充的 .trash 越多,删除项目的时间和重试次数就越多,因此您会遇到性能问题;正如您提到的那样,它有点像 "leak",但它不会影响功能。该目录应该被清空并删除,因为它包含不再需要的垃圾。系统将尝试删除它,但可能无法删除。所以使用 1).


更新 2:正如我在其中一条评论中提到的,其中一项关键更改在文件 conda/gateways/disk/__init__.py 中,它是 "fix"( https://github.com/conda/conda/commit/6cb3be39aec1c738678ae27b3a264941d08c859a) that made it to 4.3.6 version of conda (conda 4.3.6 release info) 解决了有问题的警告。
看不到该警告的关键在于拥有一个明确捕获并处理之前解释过的错误类型的分支。现在,当发生 ENOTEMPTY 类型的错误时(在本例中是触发打印警告的错误),这将被捕获并且不会转到打印问题检查的警告的分支。 要了解主要区别,在版本 4.3.4 中是

elif e.errno in (ENOENT, EPROTOTYPE):                   
     raise
else:
     log.warn("Uncaught backoff with errno %d", e.errno)
     raise

在版本中离子 4.3.6 它变成了:

elif e.errno in (ENOENT, ENOTEMPTY):    
    raise
else:
    log.warn("Uncaught backoff with errno %s %d", errorcode[e.errno], e.errno)
    raise

并且您清楚地看到现在该错误不会进入 else 分支,因此在这种情况下您不会看到该消息。

这些警告已经出现在我的 conda 版本 4.3.4 和 4.3.5 中,但在 4.2.x 版本中没有,在最新版本 (4.3.6) 中也没有。

我想解决 "fix" 问题的最佳方法是更新 conda:

$ conda update conda

或降级到 4.2:

$ conda install conda=4.2

两个版本的警告都消失了。