Como Hacer Una Actividad Sugar

Depurar Actividades Sugar

Introducción

No importa que tan cuidadoso seas, es bastante probable que tu Actividad no vaya a funcionar perfectamente la primera vez que la pruebes. Depurar una Actividad Sugar es un poco diferente a la depuración de un programa independiente o autónomo. Cuando pruebas un programa independiente, tú solo debes ejecutar el programa en sí. Si hay errores de sintaxis en el código puedes encontrar los mensajes de error de inmediato en la consola, y si se está ejecutando en el IDE de Eric, la última línea de código será seleccionada en el editor para que puedas corregirla y seguir adelante.

Con Sugar es un poco diferente. Es el ambiente Sugar, no el de Eric, el que ejecuta el programa. Si hay errores de sintaxis en el código tú no puedes verlos de inmediato. En cambio, el ícono de Actividad intermitente que se ve cuando tu Actividad se inicia, sólo seguirá parpadeando durante varios minutos y luego simplemente desaparecerá y tu Actividad no se iniciará. La única manera de encontrar el error que causó el problema será utilizar la Actividad Registro. Si tu programa no tiene errores de sintaxis, pero tiene errores de lógica, tú no seras capaz de dar un paso a través de tu código con un depurador para encontrarlos. En su lugar, tendrás que utilizar algún tipo de registro para rastrear a través de él lo que está sucediendo en el código y de nuevo utilizar la Actividad Registro para ver los mensajes de seguimiento. Ahora sería un buen momento para repetir algunos consejos que di antes:

Haz una versión independiente de tu programa

Haga lo que haga tu Actividad, es casi seguro que el 80% de ella lo podría realizar un programa independiente, el cual es mucho menos tedioso para depurar. Si tú puedes pensar en una manera de hacer tu Actividad ejecutable, sea como una Actividad independiente o un programa Python autónomo, trata de hacerlo por todos los medios.

 

Usa PyLint, PyChecker, o PyFlakes

Una de las ventajas de un lenguaje compilado como C sobre un lenguaje interpretado como Python es que el compilador realiza una comprobación de sintaxis completa del código antes de convertirlo en lenguaje de máquina. Si hay errores de sintaxis el compilador te da mensajes de error informativos y se detiene la compilación. Hay una utilidad llamada lint que los programadores de C pueden utilizar para hacer controles más profundos que los que hace el compilador  y encontrar cosas dudosas en el código.

Python no tiene un compilador, sino que tiene varias utilidades (como la utilididad lint) que se pueden ejecutar en el código antes de probarlo. Estas utilidades son pyflakes, pychecker y pylint. Cualquier distribución de Linux debe tener las tres disponibles.

PyFlakes

Aquí hay un ejemplo del uso de PyFlakes:

pyflakes minichat.py
minichat.py:25: 'COLOR_BUTTON_GREY' imported but unused
minichat.py:28: 'XoColor' imported but unused
minichat.py:29: 'Palette' imported but unused
minichat.py:29: 'CanvasInvoker' imported but unused

PyFlakes parece hacer el menor control de los tres, pero encuentra errores como estos de arriba que podrían pasar desapercibidos al ojo humano .

PyChecker

Aquí está PyChecker en acción: 

pychecker ReadEtextsActivity.py
Processing ReadEtextsActivity...
/usr/lib/python2.5/site-packages/dbus/_dbus.py:251:
DeprecationWarning: The dbus_bindings module is not public
API and will go away soon.

Most uses of dbus_bindings are applications catching
the exception dbus.dbus_bindings.DBusException.
You should use dbus.DBusException instead (this is
compatible with all dbus-python versions since 0.40.2).

If you need additional public API, please contact
the maintainers via <dbus@lists.freedesktop.org>.

  import dbus.dbus_bindings as m

Warnings...

/usr/lib/python2.5/site-packages/sugar/activity/activity.py:847:
Parameter (ps) not used
/usr/lib/python2.5/site-packages/sugar/activity/activity.py:992:
Parameter (event) not used
/usr/lib/python2.5/site-packages/sugar/activity/activity.py:992:
Parameter (widget) not used
/usr/lib/python2.5/site-packages/sugar/activity/activity.py:996:
Parameter (widget) not used

/usr/lib/python2.5/site-packages/sugar/graphics/window.py:157:
No class attribute (_alert) found
/usr/lib/python2.5/site-packages/sugar/graphics/window.py:164:
Parameter (window) not used
/usr/lib/python2.5/site-packages/sugar/graphics/window.py:188:
Parameter (widget) not used
/usr/lib/python2.5/site-packages/sugar/graphics/window.py:200:
Parameter (event) not used
/usr/lib/python2.5/site-packages/sugar/graphics/window.py:200:
Parameter (widget) not used

ReadEtextsActivity.py:62: Parameter (widget) not used

4 errors suppressed, use -#/--limit to increase the number
of errors displayed

PyChecker no sólo comprueba tu código, también comprueba el código que se importa, incluyendo el código de Sugar.

PyLint

Aquí está PyLint, la más completa de las tres:

pylint ReadEtextsActivity.py
No config file found, using default configuration
************* Module ReadEtextsActivity
C:177: Line too long (96/80)
C:  1: Missing docstring
C: 27: Operator not preceded by a space
page=0
    ^
C: 27: Invalid name "page" (should match
(([A-Z_][A-Z0-9_]*)|(__.*__))$)
C: 30:ReadEtextsActivity: Missing docstring
C:174:ReadEtextsActivity.read_file: Invalid name "zf" (should
match [a-z_][a-z0-9_]{2,30}$)
W: 30:ReadEtextsActivity: Method 'write_file' is abstract
in class 'Activity' but is not overridden
R: 30:ReadEtextsActivity: Too many ancestors (12/7)
W: 33:ReadEtextsActivity.__init__: Using the global statement
R: 62:ReadEtextsActivity.keypress_cb:
Too many return statements (7/6)
C: 88:ReadEtextsActivity.page_previous: Missing docstring
W: 89:ReadEtextsActivity.page_previous:
Using the global statement
C: 90:ReadEtextsActivity.page_previous:
Operator not preceded by a space
        page=page-1
            ^
C: 91:ReadEtextsActivity.page_previous:
Operator not preceded by a space
        if page < 0: page=0
                         ^
C: 91:ReadEtextsActivity.page_previous: More than one
statement on a single line
C: 96:ReadEtextsActivity.page_next: Missing docstring
W: 97:ReadEtextsActivity.page_next: Using the global
statement
C: 98:ReadEtextsActivity.page_next: Operator not preceded
by a space
        page=page+1
            ^
C: 99:ReadEtextsActivity.page_next: More than one
statement on a single line
C:104:ReadEtextsActivity.font_decrease: Missing docstring
C:112:ReadEtextsActivity.font_increase: Missing docstring
C:118:ReadEtextsActivity.scroll_down: Missing docstring
C:130:ReadEtextsActivity.scroll_up: Missing docstring
C:142:ReadEtextsActivity.show_page: Missing docstring
W:143:ReadEtextsActivity.show_page: Using global for
'PAGE_SIZE' but no assigment is done
W:143:ReadEtextsActivity.show_page: Using global for
'current_word' but no assigment is done
W:157:ReadEtextsActivity.save_extracted_file: Redefining
name 'zipfile' from outer scope (line 21)
C:163:ReadEtextsActivity.save_extracted_file: Invalid
name "f" (should match [a-z_][a-z0-9_]{2,30}$)
W:171:ReadEtextsActivity.read_file: Using global
for 'PAGE_SIZE' but no assigment is done
C:177:ReadEtextsActivity.read_file: Invalid name
"currentFileName" (should match [a-z_][a-z0-9_]{2,30}$)
C:179:ReadEtextsActivity.read_file: Invalid name
"currentFileName" (should match [a-z_][a-z0-9_]{2,30}$)
C:197:ReadEtextsActivity.make_new_filename: Missing
docstring
R:197:ReadEtextsActivity.make_new_filename: Method could be
a function
R: 30:ReadEtextsActivity: Too many public methods (350/20)
W:174:ReadEtextsActivity.read_file: Attribute
'zf' defined outside __init__
W:181:ReadEtextsActivity.read_file: Attribute
'etext_file' defined outside __init__
W:175:ReadEtextsActivity.read_file: Attribute
'book_files' defined outside __init__
W:182:ReadEtextsActivity.read_file: Attribute
'page_index' defined outside __init__

... A bunch of tables appear here ...

Global evaluation
-----------------
Your code has been rated at 7.52/10 (previous run: 7.52/10)

Pylint es el más duro con tu código y tu ego. No sólo te informa acerca de los errores de sintaxis, te dice todos los errores que alguien podría encontrar en tu código. Esto incluye cuestiones de estilo que no van a afectar la forma como se ejecuta el código, sino que afectan el modo en que es legible por otros programadores. 

La Actividad Registro

Al comenzar a probar tus Actividades, la Actividad Registro (Log) será como tu segunda casa. Ella muestra una lista de archivos de registro en el panel de la izquierda y al seleccionar uno de ellos mostrará el contenido del archivo en el panel derecho. Cada vez que ejecutes tu Actividad, un nuevo archivo de registro se crea para ella, para que puedas comparar el registro que se obtuvo en esta ocasión con lo que tienes de ejecuciones anteriores. La barra de herramientas Edit (Editar) es especialmente útil. Contiene un botón para mostrar el archivo de registro con “líneas ajustadas”, es decir, justificadas (que no está activada por defecto, pero probablemente deberías hacerlo). Tiene otro botón para copiar selecciones del registro en el portapapeles, que será muy útil si deseas mostrar mensajes del registro a otros desarrolladores.

La barra de herramientas tiene un botón para borrar archivos de registro. Nunca he encontrado una razón para usarlo. Los archivos de registro se borran por sí solos al reiniciar la XO.


Esto es lo que la Actividad Registro muestra cuando detecta un error de sintaxis en el código: 


Logging (registro)

Sin lugar a dudas la técnica más antigua de depuración debe ser la declaración de un simple print. Si tú tienes un programa en ejecución que se porta mal por los errores de lógica y  no puedes recorrer el código en un depurador para averiguar lo que está sucediendo, puedes poner declaraciones print en tu código. Por ejemplo, si no estás seguro si un método llega a ser ejecutado, puedes poner una declaración como esta como la primer línea del método:

    def mi_metodo():
        print 'mi_metodo() inicia'

También puedes incluir datos en las declaraciones de print. Supongamos que necesitas saber cuántas veces se ejecuta un bucle. Tu puedes hacer esto:

        while contador < TAMANIO_PAGINA:
            linea = self.archivo_texto.readline()
            texto_etiqueta = texto_etiqueta + unicode(linea, 'iso-8859-1')
            contador = contador + 1
            print 'contador=', contador 

La salida de estos print se puede ver en la Actividad Registro. Cuando hayas terminado de depurar el programa deberías eliminar estas declaraciones.

Un viejo libro de programación que leí una vez, aconsejaba dejar las declaraciones print en el programa terminado. Los autores consideran que el uso de estas declaraciones para la depuración y la posterior eliminación de errores se parece un poco a llevar un paracaídas cuando el avión está en tierra y quitárselo cuando está en el aire. Si el programa está afuera en el mundo y tiene problemas te gustaría tener esas declaraciones en el código para que puedan ayudar al usuario y a ti mismo a descubrir lo que está pasando. Por otro lado, las declaraciones de print no son gratuitas. Ellas se toman el tiempo para correr y llenan los archivos de registro de basura. Lo que necesitamos son print que uno pueda prender y apagar.

La forma en que se puede hacerlo es con Python Standard Logging. La forma utilizada por la mayoría de las Actividades se parece a esto:

        self._logger = logging.getLogger('read-etexts-activity')

Estas declaraciones pueden ir en el método __init__ () de su actividad. Cada vez que quieras hacer un print puedes hacer en su lugar:

    def _shared_cb(self, activity):
        self._logger.debug('Mi actividad fue compartida')
        self.iniciando = True
        self._sharing_setup()

        self._logger.debug('Esta es mi actividad: haciendo un tubo...')
        id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].\
            OfferDBusTube(SERVICE, {})

    def _sharing_setup(self):
        if self._shared_activity is None:
            self._logger.error(
                'Failed compartir o unirse a la actividad')
            return

Ten en cuenta que hay dos tipos de registro aquí: de “depuración” y de “error” (debug y error respectivamente). Estos son niveles de error. Cada sentencia tiene uno, y controlan que declaraciones de control se ejecutan y cuales son ignoradas. Hay varios niveles de registro de errores, desde la severidad más baja a la más alta:


    self._logger.debug("mensaje de depuracion")
    self._logger.info("mensaje de informacion")
    self._logger.warn("mensaje de advertencia")
    self._logger.error("mensaje de errorr")
    self._logger.critical("mensaje critico")

Cuando estableces el nivel de error en tu programa a uno de estos valores, se reciben mensajes con ese nivel y más alto. Tú puedes ajustar el nivel en el código del programa así:

    self._logger.setLevel(logging.DEBUG)

También puedes establecer el nivel de registro fuera del código de tu programa con una variable de entorno. Por ejemplo, en la versión de Sugar 0.82 e inferiores, puedes empezar un emulador de Sugar de la siguiente manera:

SUGAR_LOGGER_LEVEL=debug sugar-emulator

Tu puedes hacer lo mismo en la versión 0.84 y posteriores, pero hay una manera más conveniente. Edita el archivo ~/.sugar/debug y descomenta la línea que establece el SUGAR_LOGGER_LEVEL. Sea cual sea el valor que tiene  SUGAR_LOGGER_LEVEL en ~/.sugar/debug anulará el establecido por la variable de entorno, por lo tanto, hay que cambiar la configuración en el archivo o utilizar la variable de entorno, pero no hagas ambas. 

La Actividad Analyze

Otra Actividad que utilizarás en algún momento es Analyze. Esta Actividad es más probable que la utilices para la depuración de Sugar en sí mismo que para depurar tu Actividad. Si por ejemplo, el entorno de prueba de la colaboración no parece estar funcionando, esta Actividad podría ayudarte a ti o a alguien más a encontrar por qué.

No tengo mucho que decir acerca de esta Actividad aquí, pero por lo menos tu debes saber que existe. 


1 
 

  1. Traducido Alan Aguiar, Uruguay^