Gran pérdida de memoria en repetidas llamadas os.path.isdir?

He estado escribiendo algo que tiene que ver con la exploración de directorios y noté una pérdida de memoria grave al llamar a os.path.isdir, por lo que probé el siguiente fragmento de código:

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

En unos pocos segundos, el proceso de Python alcanzó los 100 MB de RAM.

Estoy tratando de averiguar qué está pasando. Parece que la enorme fuga de memoria está en efecto solo cuando la ruta es de hecho una ruta de directorio válida (lo que significa que no se ejecuta el ‘retorno Falso’). Además, es interesante ver qué sucede en llamadas relacionadas, como os.path.isfile.

¿Pensamientos?

Edit: Creo que estoy en algo. Aunque isfile y isdir se implementan en el módulo de ruta de acceso genérico, en el sistema de Windows, isdir se está importando desde la versión incorporada. Así que tuve que descargar la fuente 2.7.3 (que debería haber hecho hace mucho tiempo …).

    Después de un poco de búsqueda, descubrí la función posix__isdir en \ Modules \ posixmodule.c , que asumo es la función ‘isdir’ importada de nt.

    Esta parte de la función (y el comentario) me llamó la atención:

     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(); 

    Parece que todo se reduce a un error de manejo de Unicode / ASCII.

    Acabo de probar mi fragmento de código anterior con el argumento de ruta en Unicode (es decir, u’D: \ Downloads ‘): no hay pérdida de memoria alguna. jaja.

    La causa raíz es un error al llamar a PyMem_Free en la variable de path en la path no 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; 

    Según la documentación en PyArg_ParseTuple :

    • et : igual que es
    • es : PyArg_ParseTuple() asignará un búfer del tamaño necesario, copiará los datos codificados en este búfer y ajustará * el búfer para hacer referencia al almacenamiento recién asignado. La persona que llama es responsable de llamar a PyMem_Free() para liberar el búfer asignado después de su uso .

    Es un error en la biblioteca estándar de Python (corregido en Python 3 utilizando objetos de bytes directamente); presentar un informe de error en http://bugs.python.org .