Подтвердить что ты не робот

Огромная утечка памяти при повторных вызовах os.path.isdir?

Я пишу скрипт, который связан с сканированием каталогов, и заметил серьезную утечку памяти при вызове os.path.isdir, поэтому я пробовал следующий фрагмент:

def func():
    if not os.path.isdir('D:\Downloads'):
        return False
while True:
    func()

В течение нескольких секунд процесс Python достиг 100 МБ ОЗУ.

Я пытаюсь понять, что происходит. Похоже, что огромная утечка памяти действует только тогда, когда путь действительно является допустимым пути к каталогу (это означает, что "return False" не выполняется). Кроме того, интересно посмотреть, что происходит в связанных вызовах, например os.path.isfile.

Мысли?

Edit: Я думаю, что на что-то. Хотя isfile и isdir реализованы в модуле genericpath, в системе Windows - isdir импортируется из встроенного nt. Поэтому мне пришлось загрузить источник 2.7.3 (который я должен был сделать давно...).

После небольшого поиска я обнаружил функцию posix__isdir в \Modules\posixmodule.c, которая, как я полагаю, является функцией isdir, импортированной из nt.

Эта часть функции (и комментариев) привлекла мое внимание:

if (PyArg_ParseTuple(args, "U|:_isdir", &po)) {
        Py_UNICODE *wpath = PyUnicode_AS_UNICODE(po);

        attributes = GetFileAttributesW(wpath);
        if (attributes == INVALID_FILE_ATTRIBUTES)
            Py_RETURN_FALSE;
        goto check;
    }
    /* Drop the argument parsing error as narrow strings
       are also valid. */
    PyErr_Clear();

Кажется, что все это сводится к ошибкам обработки Unicode/ASCII.

Я только что попробовал свой фрагмент выше с аргументом пути в unicode (т.е. u'D:\Downloads)) - никакой утечки памяти вообще нет. ха-ха.

4b9b3361

Ответ 1

Основной причиной является отказ вызова PyMem_Free в переменной path в пути, отличном от Unicode:

    if (!PyArg_ParseTuple(args, "et:_isdir",
                          Py_FileSystemDefaultEncoding, &path))
        return NULL;

    attributes = GetFileAttributesA(path);
    if (attributes == INVALID_FILE_ATTRIBUTES)
        Py_RETURN_FALSE;

check:
    if (attributes & FILE_ATTRIBUTE_DIRECTORY)
        Py_RETURN_TRUE;
    else
        Py_RETURN_FALSE;

В соответствии с документацией на PyArg_ParseTuple:

  • et: То же, что es...
  • es: PyArg_ParseTuple() выделяет буфер необходимого размера, копирует закодированные данные в этот буфер и корректирует * буфер для ссылки на вновь выделенное хранилище. Вызывающий отвечает за вызов PyMem_Free() для освобождения выделенного буфера после использования.

Это ошибка в стандартной библиотеке Python (исправлена ​​в Python 3 с использованием объектов байтов напрямую); напишите отчет об ошибке в http://bugs.python.org.