python - Enorme pérdida de memoria en repetidas llamadas os.path.isdir?
memory-leaks python-2.7 (1)
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 gran pérdida de memoria tiene 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 supongo que 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 quees
...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 aPyMem_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 usando objetos de bytes directamente); presentar un informe de error en http://bugs.python.org .