type escape python unicode python-3.x cgi

type - unicode escape python



Establecer codificación en Python 3 CGI scripts (5)

Cuando escribo un script CGI de Python 3.1 , me encuentro con horribles UnicodeDecodeErrors. Sin embargo, cuando se ejecuta el script en la línea de comandos, todo funciona.

Parece que open() y print() usan el valor de retorno de locale.getpreferredencoding() para saber qué codificación usar de forma predeterminada. Cuando se ejecuta en la línea de comando, ese valor es ''UTF-8'', como debería ser. Pero cuando se ejecuta el script a través de un navegador, la codificación se redefine misteriosamente a ''ANSI_X3.4-1968'', que parece ser solo un nombre elegante para ASCII simple.

Ahora necesito saber cómo hacer que el script cgi se ejecute con ''utf-8'' como la codificación predeterminada en todos los casos. Mi configuración es Python 3.1.3 y Apache2 en Debian Linux. La configuración regional de todo el sistema es en_GB.utf-8.


Lo mejor que puede hacer es codificar explícitamente sus cadenas Unicode en bytes utilizando la codificación que desea usar. Confiar en la conversión implícita dará lugar a problemas como este.

Por cierto: si el error es realmente UnicodeDecodeError, entonces no está sucediendo en la salida, está intentando decodificar un flujo de bytes en Unicode, lo que ocurriría en otro lugar.


No debe leer sus flujos de IO como cadenas para CGI / WSGI; no son cadenas de Unicode, son explícitamente secuencias de bytes.

(Tenga en cuenta que Content-Length se mide en bytes y no en caracteres; imagine que intenta leer un envío de carga de archivos binarios de varias multipart/form-data formados en cadenas decodificadas UTF-8, o devolver una descarga de archivos binarios ...)

Entonces, en lugar de eso, use sys.stdin.buffer y sys.stdout.buffer para obtener los flujos de bytes sin formato para stdio, y lea / escriba binario con ellos. Depende de la capa de lectura de formularios convertir esos bytes en parámetros de cadena Unicode, según corresponda, utilizando la codificación que tenga su página web.

Desafortunadamente, las interfaces CGI y WSGI de la biblioteca estándar no lo consiguen en Python 3.1: los módulos relevantes se convirtieron de forma cruda a partir de los originales de Python 2 usando 2to3 y, por lo tanto, hay una serie de errores que terminarán en UnicodeError.

La primera versión de Python 3 que se puede usar para aplicaciones web es 3.2. Usar 3.0 / 3.1 es prácticamente una pérdida de tiempo. Me tomó un tiempo lamentablemente largo resolverlo y pasar PEP3333.


Resolví mi problema con el siguiente código:

import locale # Ensures that subsequent open()s locale.getpreferredencoding = lambda: ''UTF-8'' # are UTF-8 encoded. import sys sys.stdin = open(''/dev/stdin'', ''r'') # Re-open standard files in UTF-8 sys.stdout = open(''/dev/stdout'', ''w'') # mode. sys.stderr = open(''/dev/stderr'', ''w'')

Esta solución no es bonita, pero parece funcionar por el momento. De hecho, elegí Python 3 en lugar del v.6 más común como mi plataforma de desarrollo debido a la buena gestión publicitaria de Unicode, pero el paquete cgi parece arruinar algo de esa simplicidad.

Me llevan a creer que los archivos /dev/std* pueden no existir en sistemas más antiguos que no tienen un procfs . Sin embargo, son compatibles con Linux recientes.


Respondiendo esto para los que llegan tarde porque no creo que las respuestas publicadas lleguen a la raíz del problema, que es la falta de variables de entorno de entorno local en un contexto CGI. Estoy usando Python 3.2.

  1. open () abre objetos de archivo en modo de texto (cadena) o binario (bytes) para lectura y / o escritura; en el modo de texto, la codificación utilizada para codificar las cadenas escritas en el archivo y la decodificación de los bytes leídos en el archivo, puede especificarse en la llamada; si no lo está, entonces está determinado por locale.getpreferredencoding (), que en linux usa la codificación de la configuración de su entorno local, que normalmente es utf-8 (de, por ejemplo, LANG = en_US.UTF-8)

    >>> f = open(''foo'', ''w'') # open file for writing in text mode >>> f.encoding ''UTF-8'' # encoding is from the environment >>> f.write(''€'') # write a Unicode string 1 >>> f.close() >>> exit() user@host:~$ hd foo 00000000 e2 82 ac |...| # data is UTF-8 encoded

  2. sys.stdout es, de hecho, un archivo abierto para escribir en modo de texto con una codificación basada en locale.getpreferredencoding (); puede escribir cadenas en él muy bien y se codificarán en bytes según la codificación de sys.stdout; print () por defecto escribe en sys.stdout - print () no tiene codificación, sino que es el archivo en el que se escribe que tiene una codificación;

    >>> sys.stdout.encoding ''UTF-8'' # encoding is from the environment >>> exit() user@host:~$ python3 -c ''print("€")'' > foo user@host:~$ hd foo 00000000 e2 82 ac 0a |....| # data is UTF-8 encoded; /n is from print()

    ; no puede escribir bytes en sys.stdout; use sys.stdout.buffer.write () para eso; Si intenta escribir bytes en sys.stdout utilizando sys.stdout.write (), devolverá un error, y si intenta usar print (), print () simplemente convertirá el objeto de bytes en un objeto de cadena y un escape. la secuencia como /xff se tratará como los cuatro caracteres /, x, f, f

    user@host:~$ python3 -c ''print(b"/xe2/xf82/xac")'' > foo user@host:~$ hd foo 00000000 62 27 5c 78 65 32 5c 78 66 38 32 5c 78 61 63 27 |b''/xe2/xf82/xac''| 00000010 0a |.|

  3. en un script CGI debe escribir en sys.stdout y puede usar print () para hacerlo; pero un proceso de script CGI en Apache no tiene configuraciones de entorno de entorno local, no son parte de la especificación CGI; por lo tanto, la codificación de sys.stdout se establece de manera predeterminada en ANSI_X3.4-1968; en otras palabras, ASCII; si intenta imprimir () una cadena que contiene caracteres no ASCII para sys.stdout obtendrá "UnicodeEncodeError: el códec ''ascii'' no puede codificar el carácter ...: ordinal no está dentro del rango (128)"

  4. una solución simple es pasar la variable de entorno LANG del proceso Apache a la secuencia de comandos CGI utilizando el comando mod_env PassEnv de Apache en el servidor o configuración de host virtual: PassEnv LANG; en Debian / Ubuntu, asegúrese de que en / etc / apache2 / envvars no haya comentado la línea ". / etc / default / locale" para que Apache se ejecute con la configuración regional predeterminada del sistema y no con la configuración regional C (Posix) (que también es ASCII codificación); el siguiente script CGI debería ejecutarse sin errores en Python 3.2:

    #!/usr/bin/env python3 import sys print(''Content-Type: text/html; charset=utf-8'') print() print(''<html><body><pre>'' + sys.stdout.encoding + ''</pre>h€lló wörld<body></html>'')


Resumiendo la respuesta de @cercatrova:

  • Agregue la línea PassEnv LANG al final de su /etc/apache2/apache2.conf o .htaccess .
  • Descomentar . /etc/default/locale line en /etc/apache2/envvars .
  • Asegúrese de que la línea similar a LANG="en_US.UTF-8" esté presente en /etc/default/locale .
  • sudo service apache2 restart