Modo de aplicación de archivos PyPy

Tengo un código como este:

f1 = open('file1', 'a') f2 = open('file1', 'a') f1.write('Test line 1\n') f2.write('Test line 2\n') f1.write('Test line 3\n') f2.write('Test line 4\n') 

Cuando este código se ejecuta con el intérprete estándar de Python 2.7, el archivo contiene cuatro líneas como se esperaba. Sin embargo, cuando ejecuto este código en PyPy, el archivo contiene solo dos líneas.

¿Alguien podría explicar las diferencias entre Python y PyPy al trabajar con archivos en modo de adición?

ACTUALIZADO : El problema no existe en PyPy 2.3.

La razón en el comportamiento diferente es la implementación diferente de las operaciones de E / S de archivos.

CPython implementa su archivo I / O encima de las funciones fopen , fread y fwrite de stdio.h y está trabajando con flujos FILE * .

Al mismo tiempo, PyPy implementa su E / S de archivos sobre POSIX funciones de open , write y read y está trabajando con descriptores de archivos int .

Compara estos dos progtwigs en C:

 #include  int main() { FILE *a = fopen("file1", "a"); FILE *b = fopen("file1", "a"); fwrite("Test line 1\n", 12, 1, a); fflush(a); fwrite("Test line 2\n", 12, 1, b); fflush(b); fwrite("Test line 3\n", 12, 1, a); fflush(a); fwrite("Test line 4\n", 12, 1, b); fclose(a); fclose(b); return 0; } 

y

 #include  #include  int main() { int a = open("file1", O_CREAT | O_WRONLY | O_APPEND); int b = open("file1", O_CREAT | O_WRONLY | O_APPEND); write(a, "Test line 1\n", 12); write(b, "Test line 2\n", 12); write(a, "Test line 3\n", 12); write(b, "Test line 4\n", 12); close(a); close(b); return 0; } 

Puede encontrar más información sobre la diferencia entre open y open en las respuestas a esta pregunta .

ACTUALIZACIÓN :

Después de inspeccionar el código base de PyPy un poco más, me parece que no usa la bandera O_APPEND por algún motivo, pero O_WRONLY | O_CREAT O_WRONLY | O_CREAT para el modo “a”. Así que es la verdadera razón por la que en PyPy necesitas seek el final del archivo después de cada llamada de write , como lo mencionó JF Sebastian en otra respuesta . Supongo que se debe crear un error en PyPy bugtracker, ya que el indicador O_APPEND está disponible tanto en Windows como en Unix. Entonces, lo que hace PyPy ahora parece:

 #include  #include  int main() { int a = open("file1", O_CREAT | O_WRONLY); int b = open("file1", O_CREAT | O_WRONLY); write(a, "Test line 1\n", 12); write(b, "Test line 2\n", 12); write(a, "Test line 3\n", 12); write(b, "Test line 4\n", 12); close(a); close(b); return 0; } 

Sin el indicador O_APPEND debería reproducir el comportamiento de PyPy.

En los sistemas POSIX :

O_APPEND
Si se establece, el desplazamiento del archivo se establecerá al final del archivo antes de cada escritura.

Esto significa que si un archivo se abre en modo “agregar”, entonces cuando se vacía su búfer; El contenido irá al final del archivo.

Python 2, Python 3, Jython respetan eso en mi máquina. En su caso, el contenido es más pequeño que el búfer del archivo, por lo tanto, verá todas las escrituras de un archivo seguidas de todas las escrituras de otro archivo en el resultado en el disco.

Es más fácil de entender si los archivos tienen búfer de línea:

 from __future__ import with_statement filename = 'file1' with open(filename, 'wb', 0) as file: pass # truncate the file f1 = open(filename, 'a', 1) f2 = open(filename, 'a', 1) f1.write('f1 1\n') f2.write('f2 aa\n') f1.write('f1 222\n') f2.write('f2 bbbb\n') f1.write('f1 333\n') f2.write('f2 cc\n') 

Salida

 f1 1 f2 aa f1 222 f2 bbbb f1 333 f2 cc 

La documentación de Python no exige tal comportamiento. Simplemente menciona :

.. ‘a’ para anexar (lo que en algunos sistemas Unix significa que todas las escrituras se anexan al final del archivo, independientemente de la posición de búsqueda actual ), el énfasis es mío

Pypy produce la siguiente salida en modo sin búfer y con búfer de línea:

 f2 aaff2 bbbf1f2 cc 

Moviendo manualmente la posición del archivo al final arregla la salida pypy:

 from __future__ import with_statement import os filename = 'file1' with open(filename, 'wb', 0) as file: pass # truncate the file f1 = open(filename, 'a', 1) f2 = open(filename, 'a', 1) f1.write('f1 1\n') f2.seek(0, os.SEEK_END) f2.write('f2 aa\n') f1.seek(0, os.SEEK_END) f1.write('f1 222\n') f2.seek(0, os.SEEK_END) f2.write('f2 bbbb\n') f1.seek(0, os.SEEK_END) f1.write('f1 333\n') f2.seek(0, os.SEEK_END) f2.write('f2 cc\n') 

Si el archivo está completamente en el búfer, agregue .flush() después de cada .write() .

Probablemente no sea una buena idea escribir en el mismo archivo utilizando más de un objeto de archivo a la vez.