miércoles, 18 de mayo de 2011

martes, 3 de mayo de 2011

Características de Python 2.7 obsoletas en 3.x

Una reciente discusión en python-dev puso de manifiesto un fallo en la actual política de obsolescencia (deprecation) adoptada por los desarrolladores en la migración de 2.7 a la nueva 3.x. En consecuencia, el equipo de desarrollo ha tenido que modificar su política para tener en cuenta que los usuarios migrarán directamente de Python 2.7 a la última versión de 3.x, sin pasar por versiones intermedias.

Antecedentes

Python tiene un fuerte compromiso con la compatibilidad con versiones anteriores. No se permite ningún cambio a menos que esté de acuerdo con las directrices de compatibilidad, que en esencia dicen que programas correctos no deben fallar en versiones más modernas. Sin embargo, esto no es siempre posible, por ejemplo, cuando una API deja de funcionar y debe ser reemplazada por algo nuevo. En este caso, Python sigue una política de obsolescencia basada en un periodo de transición de un año en el que las características a renovar son consideradas formalmente obsoletas. En el periodo intermedio, se debe emitir un aviso (deprecation warning) de forma que los programadores tengan tiempo de actualizar su código. Los detalles completos de la política de Python están documentados en el PEP 5. Los cambios sólo se hacen en las versiones nuevas y normalmente pasan 18 meses entre versiones; ergo la norma es una versión en el periodo.

La única excepción ha sido Python 3. La nueva rama ha sido específicamente creada para permitir cambios que rompieran la compatibilidad, permitiendo así a los desarrolladores de Python corregir problemas que, simplemente, no podían solucionarse dentro de la política actual. Por ejemplo, hacer las cadenas Unicode por defecto y devolver iteradores en lugar de listas.

Líneas paralelas de desarrollo

Sabiendo que la transción a Python 3 llevaría tiempo (unos 5 años, según varias estimaciones), habría cierto desarrollo paralelo entre 2 y 3.

Con Python 2.7 como versión final de Python 2, se acordó que el periodo de mantenimiento se extendería por un periodo substancial. Tarde o temprano, los desarrolladores que quieran migrar a versiones más nuevas deberán dar el salto a Python 3.

He aquí uno de los problemas...

Obsolescencias sorpresa

En un hilo en python-dev`<http://mail.python.org/pipermail/python-dev/2011-March/109010.html>`__, se señaló que una función específica de la API C, PyCObject_AsVoidPtr, había sido eliminada con lo que parecían insuficientes avisos. Y, sin embargo, la política adoptada se supone que debería proteger contra eso. ¿Qué pasó?

El cambio fue parte de una migración mayor desde una API más antigua, PyCObject, a una nueva y mejorada, PyCapsule. El problema es que PyCObject es la predeterminada, y desde luego, la única API disponible en Python 2.6. En 2.7 pasó a estar considerada obsoleta, y en Python 3.2 no existe, y la nueva PyCapsule debe ser usada. Esto supone un periodo de tránsito entre el lanzamiento de 2.7 (julio del 2010) al lanzamiento de 3.2 (febrero del 2011) de siete meses; mucho menor al mínimo de doce meses exigido, y hace más difícil dar soporte a un rango razonable de versiones de Python.

Para alguien migrando de 3.0 a 3.1 y después a 3.2, el tránsito es correcto. Python 3.1 fue lanzado en marzo del 2010 con la marca de obsolescencia, por tanto, en la serie 3.x hay un periodo de casi 12 meses. Sin embargo, esto no es lo habitual: migran de 2.7 directamente a la última versión de 3.x; en nuestro caso, 3.2, apareciendo este problema. Nunca fue la intención del equipo de desarrollo, pero PEP 5 no se escribió para desarrollos de dos versiones en paralelo.

Entonces, ¿qué hacemos?

Dado que el problema con la API PyCObject/PyCapsule está bien definido, no es imposible de evitar, pero al menos una persona de python-dev ha tenido algunos problemas con ello. En resumen, esto no debería haber ocurrido.

Para el caso específico de PyCObject/PyCapsule, el problema ya existía y no había mucho que se pudiera hacer. Reincorporar PyCObject no era una opción, dado que sólo añadiría nuevas incompatibilidades. Sin embargo, la visión general fue que era posible, aunque tedioso, escribir código que se adaptara a cualquier API disponible. De hecho, en Python 3.1, la API PyCObject fue escrita como un wrapper de la PyCapsule. Había una sugerencia de que, en caso de ser necesitada, el código de la implementación en 3.1 podría ser extraído y ser usado como código para terceros. Adicionalmente, se acordó que se escribiría un PEP "retroactivo" cubriendo el cambio, para describir las razones tras el cambio y documentar los recursos que ayudarían a los programadores a migrar.

El equipo de Python ahora está al tanto de los problemas y trabajará para evitar que vuelvan a ocurrir. Guido publicó una reseña de la situación y sugirió que Python 3 debería ser conservativo dejando características obsoletas por el momento. Como mínimo, las API consideradas obsoletas se conservarán más tiempo antes de ser eliminadas, para dar a los programadores migrando desde 2.7 un camino más fácil.

Más indirectamente, el hilo mostró el problema de cómo comunicar de forma más efectiva los cambios en Python a una audiencia mayor, de forma más oportuna; una de las razones de ser de este blog.

¿Qué significa todo esto?

En primer lugar, significa que los desarrolladores de Python no lo hacen todo bien. Nadie quiere hacer la vida de los programadores más difícil, simplemente no fue algo que se viera a tiempo.

En segundo lugar, arreglar el problema puede hacer aún más daño, así que PyCObject no se reincorporará. Aunque eso ayudaría a los programadores afectados por el cambio, en global haría los problemas de compatibilidad más complejos. Mientras tanto, tendremos que sobreponernos al problema y seguir adelante. La lección está aprendida, y no volveremos a cometer el mismo error de nuevo.

Una hecho positivo puesto de manifiesto es que el equipo de Python escucha lo que sus usarios tienen que decirles. La compatibilidad es muy importante, y se pone todo el esfuerzo en hacer la transición a nuevas versiones tan indolora como sea posible. En particular, los desarrolladores de bibliotecas deberían ser capaces de soportar varias versiones de Python con un nivel adecuado de esfuerzo.

Finalmente, los desarrolladores no han abandonado 2.7. No se implementarán nuevas características y no existirá 2.8, pero la visión de los usuarios de 2.7 todavía es importante. Estar seguros de que los usuarios pueden migrar a 3.x cuando estén listos es de vital importancia para la comunidad de Python.