Ответ 1
Эти предупреждения, которые вы видите, являются "нормальными" в соответствии с текущим деревом источника conda. Чтобы понять происхождение вышеупомянутых предупреждений, давайте посмотрим на исходный код в вопросах и недавнюю фиксацию в репозитории 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, которое может появиться, когда
повторное выполнение файловых операций, которые не выполняются в Windows из-за вирусных сканеров
Переходя к особенностям с помощью https://msdn.microsoft.com/en-us/library/t3ayayh1.aspx, кажется, что errno 41 соответствует
ENOTEMPTY: каталог не пуст
который сигнализирует, что указанный каталог не пуст.
Это непрозрачная ошибка, потому что у них нет ветки, которая имеет дело с такой ошибкой (ENOTEMPTY), в то время как, например, у них есть другая ошибка, например, EPERM или EACCES.
В commit https://github.com/conda/conda/commit/fb2a783d9b9371559b5ea82aaf8ae631c2ce0450#diff-3757ed9862260ae3b54768b3e482e3fe
они явно удаляют отчетность EPROTOTYPE
как OSError(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()
, поэтому теперь, если вы включите информационный журнал, скорее всего, вы увидите строку типа
"Невозможно полностью очистить каталог мусора% s\nУдаются% d оставшихся файлов."
включен
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
В соответствии с изменениями исходного кода, внесенными в репозиторий, разработчики полагают, что они предупреждают о том, что вы можете спокойно игнорировать, поскольку эти предупреждения происходят на этапе "очистки" команд обновления или установки, то есть после операции обновления или установки успешно выполнены.
Я не мог сказать 100%, что вы можете смело игнорировать эти предупреждения. Если после нескольких попыток команда удалять каталог мусора завершается успешно, тогда проблем нет. Но если это не удастся, вы столкнетесь с проблемой, что этот каталог будет становиться все больше и больше в результате не удалять его. Было несколько выпусков, открытых в репо для этого, и я не знаю, покрывают ли исправления код, который вы нажмете. Мое впечатление, что это не так. Чтобы получить дополнительную информацию, вы можете активировать уровень информационного журнала.
ОБНОВЛЕНИЕ: этот вопрос https://github.com/conda/conda/issues/4164 точно упоминает предупреждения, о которых вы сообщали, потому что люди получали длительное обновление и время установки из-за всех попыток. Поскольку я упоминал, что после всех попыток (экспоненциальных отступлений) операция удаления может быть успешной или неудачной, этот человек также упоминает этот аспект в своем отчете.
Как вы можете видеть здесь
https://github.com/conda/conda/issues/3664
есть несколько хаков, которые люди используют для решения проблемы длительных ожиданий из-за повторных попыток, а также будет выполнять следующий запуск вашей команды conda install X
или conda update X
без предупреждения. Эти предложения:
- (для ускорения повтора/удаления) установите
MAX_TRIES = 1
в свою копию conda/gateways/disk/ init. py - удалите каталог .trash, прежде чем запускать следующий
conda install X
илиconda update X
. См. https://github.com/conda/conda/issues/3664 для обходного пути, используемого некоторыми людьми для автоматизации удаления этого каталога с использованием простых скриптов. Это должно быть безопасно для этого.
Таким образом, ответы на ваши вопросы будут следующими:
1) Вы можете использовать обходное решение, упомянутое в https://github.com/conda/conda/issues/3664, в котором используется следующая powershell script (и другая script):
$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 будет заселено, тем больше времени и повторов будет продолжаться, чтобы удалить элементы, чтобы вы столкнулись с проблемами производительности; как вы упомянули, что это "утечка", но это не влияет на функциональность. Этот каталог должен быть опустошен и удален, так как он содержит мусор, который больше не нужен. Система попытается удалить его, но, возможно, не сможет этого сделать. Поэтому используйте 1).
ОБНОВЛЕНИЕ 2. Как упоминалось в одном из моих комментариев, одно из ключевых изменений находится в файле conda/gateways/disk/__init__.py
, который был "исправлением" (https://github.com/conda/conda/commit/6cb3be39aec1c738678ae27b3a264941d08c859a), что привело к 4.3.6 версии conda (conda 4.3.6 информация о выпуске), который решает данное предупреждение.
Ключевым моментом в том, чтобы не видеть это предупреждение, является наличие ветки, которая явно улавливает и обрабатывает описанную ранее ошибку. Теперь, когда произойдет ошибка типа 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
, поэтому вы не увидите это сообщение в этом случае.