2to3: migrando versión de Python con un ejemplo práctico

por | febrero 8, 2018

A Python 2 todavía le quedan cubiertos 2 años de mantenimiento aunque no es mala idea ir revisando nuestros scripts e irlos migrando de forma progresiva, sin prisa pero sin pausa. Lo incluyo entre mis tareas pendientes para los scripts y proyectos que me toca migrar.

Sin ir más lejos en mi repo actual My Tux tengo el script filestrings_modifier.py que copy-pasteo aquí a modo de ejemplo de script escrito en Python 2:

#!/usr/bin/env python2
"""
Author        :Julio Sanz
Website       :www.elarraydejota.com
Email         :juliojosesb@gmail.com
Description   :Script to replace, delete or add strings to a file
Dependencies  :None
Usage         :filestrings_modifier.py [file]
Example       :filestrings_modifier.py mytext.txt
               filestrings_modifier.py /home/mytext.txt
License       :GPLv3
"""

#
# IMPORTS
#

import sys, os

#
# FUNCTIONS
#

def replace_string():
    v_file = sys.argv[1]
    stringtosearch = raw_input("Please specify the text that is going to be replaced --> ")
    replacestring = raw_input("Please specify the text that will replace the word "+stringtosearch+" --> ")
    if os.path.isfile(v_file):
        with open(v_file, 'r') as f:
            data = f.read()
        newdata = data.replace(stringtosearch, replacestring)
        with open(v_file, 'w') as f:
            f.write(newdata)
    else:
        how_to_use()

def how_to_use():
    print "\nWrong usage. Please pass an existing filename (path if needed) as a parameter to the script"
    print "Usage -> filestrings_modifier.py [filename]"
    print "Examples: filestrings_modifier.py mytext.txt"
    print "          filestrings_modifier.py /home/mytext.txt"

# MAIN

if __name__ ==  '__main__':
    if len(sys.argv) == 2:
        replace_string()
    else:
        how_to_use()

Podemos utilizar 2to3 para ayudarnos a migrar este script a Python 3. Primero lanzamos una simulación (el fichero no se modifica) con:

2to3 filestrings_modifier.py

Cuyo resultado será:

...
RefactoringTool: Refactored filestrings_modifier.py
--- filestrings_modifier.py	(original)
+++ filestrings_modifier.py	(refactored)
@@ -23,8 +23,8 @@
 
 def replace_string():
     v_file = sys.argv[1]
-    stringtosearch = raw_input("Please specify the text that is going to be replaced --> ")
-    replacestring = raw_input("Please specify the text that will replace the word "+stringtosearch+" --> ")
+    stringtosearch = input("Please specify the text that is going to be replaced --> ")
+    replacestring = input("Please specify the text that will replace the word "+stringtosearch+" --> ")
     if os.path.isfile(v_file):
         with open(v_file, 'r') as f:
             data = f.read()
@@ -35,10 +35,10 @@
         how_to_use()
 
 def how_to_use():
-    print "\nWrong usage. Please pass an existing filename (path if needed) as a parameter to the script"
-    print "Usage -> filestrings_modifier.py [filename]"
-    print "Examples: filestrings_modifier.py mytext.txt"
-    print "          filestrings_modifier.py /home/mytext.txt"
+    print("\nWrong usage. Please pass an existing filename (path if needed) as a parameter to the script")
+    print("Usage -> filestrings_modifier.py [filename]")
+    print("Examples: filestrings_modifier.py mytext.txt")
+    print("          filestrings_modifier.py /home/mytext.txt")
 
 # MAIN

RefactoringTool: Files that need to be modified:
RefactoringTool: filestrings_modifier.py

Vemos que la utilidad realiza un diff del fichero original y el que quedaría con los cambios propuestos (refactored)

Si quisiéramos cambiar realmente el script utilizamos la opción -w, por ejemplo:

2to3 -w filestrings_modifier.py

Que dicha utilidad nos facilite la vida dándonos pistas sobre cómo migrar nuestros scripts no quita que realicemos tests, comprobaciones y revisión de documentación correspondiente. En este mismo ejemplo que pongo, al migrar el script a Python 3 he tenido que cambiar manualmente el shebang de la cabecera para que apunte a la versión de Python correcta con #!/usr/bin/env python3

Un buen punto de partida de documentación la podemos encontrar en la página pyporting de la web oficial de Python, así como información sobre la utilidad 2to3.