Documentación en Python

Docstrings

En artículos anteriores del tutorial de Python ya comentamos en varias ocasiones que todos los objetos cuentan con una variable especial __doc__ mediante la que indicar el propósito y uso del objeto. Estos son los llamados docstrings o cadenas de documentación.

A estos atributos se les puede asociar el texto correspondiente explícitamente, asignándolo al literal cadena correspondiente, como con cualquier otra variable. Sin embargo, por conveniencia, Python ofrece un mecanismo mucho más sencillo y es que si el primer estamento de la definición del objeto es una cadena, esta se asocia a la variable __doc__ automáticamente.

def haz_algo(arg):
    """Este es el docstring de la funcion."""
    print arg

print haz_algo.__doc__

haz_algo.__doc__ = """Este es un nuevo docstring."""

print haz_algo.__doc__

Como vemos lo interesante de estas cadenas es que, a diferencia de los comentarios normales de Python y de los comentarios de otros lenguajes, las cadenas de documentación no se eliminan del bytecode, por lo que se pueden consultar en tiempo de ejecución, usando, por ejemplo, la función help del lenguaje, o utilizando la sentencia print como en el ejemplo anterior.

>>> help(haz_algo)
Help on function haz_algo in module __main__:
haz_algo(arg)
    Este es un nuevo docstring.

Pydoc

La función help, que comentamos brevemente con anterioridad, utiliza el módulo pydoc para generar la documentación de un objeto a partir de su docstring y los docstrings de sus miembros. Este módulo, incluido por defecto con Python desde la versión 2.1, se puede importar en nuestro código Python y utilizarse programaticamente, o bien se puede utilizar como una herramienta de línea de comandos que sería el equivalente a la aplicación Javadoc del mundo Java.

pydoc puede mostrar la información como texto en la consola, tal como lo utiliza help, pero también puede generar archivos HTML como javadoc o facilitar la información a través de un pequeño servidor web incluido con el módulo.

Pydoc es muy sencillo de utilizar. Con

pydoc.py nombre1 [nombre2 …]

se muestra la documentación del tema, módulo, clase, paquete, función o palabra clave indicada de forma similar a la función help. Si el nombre es keywords, topics o modules se listarán las distintas palabras claves, temas y módulos respectivamente.

Si se pasa el flag -w, el script guardará la documentación en uno o varios archivos html en lugar de mostrarla por pantalla.

pydoc.py -w nombre1 [nombre2 …]

El flag -k sirve para buscar una determinada palabra en las sinopsis de todos los módulos disponibles. La sinopsis es la primera línea de la cadena de documentación.

pydoc.py -k xml

Con -p podemos iniciar el servidor HTTP en el puerto indicado.

pydoc.py -p puerto

Una vez hecho esto podemos acceder a la documentación de todos los módulos disponibles abriendo la página http://localhost:puerto en nuestro navegador.

Por último, mediante el flag -g podemos lanzar una interfaz gráfica para buscar documentación que utiliza el servidor HTTP para mostrar los resultados.

Epydoc y reStructuredText

El problema de pydoc es que es muy simple, y no permite añadir semántica o modificar estilos de la documentación. No podemos, por ejemplo, indicar que en una línea en concreto de entre las líneas de documentación de la función describe un parámetro de la función o mostrar un cierto término en cursiva.

Existen proyectos para generar documentación con funcionalidades más avanzadas como Docutils, Epydoc o Sphinx, aunque es necesario aprender sintaxis especiales.

Docutils es un proyecto desarrollado por David Goodger que incluye distintas herramientas para generar documentación utilizando el formato reStructuredText, un formato de texto plano creado por el mismo autor, y que es el formato más utilizado en la comunidad Python. reStructuredText se utiliza, entre otros, para la creación de los PEPs (Python Enhancement Proposals).

Sin embargo, actualmente Docutils es más indicado para generar documentos a partir de archivos de texto, y no a partir de docstrings extraídos de código fuente Python, ya que el parser encargado de este trabajo dista mucho de estar terminado.

EpyDoc es una de las herramientas de generación de documentación para Python más utilizadas. Además de texto plano y de su propio formato, llamado epytext, soporta reStructuredText y sintaxis Javadoc, cosa que los programadores Java agradecerán.

A lo largo del resto del capítulo utilizaremos reStructuredText como lenguaje de marcado y EpyDoc para generar los documentos finales.

Epydoc se puede descargar desde su página web en forma de instalador exe para Windows, paquete RPM para Fedora o similares, o en archivos zip y tar.gz que incluyen scripts de instalación: http://epydoc.sourceforge.net/. También se encuentra en los repositorios de varias distribuciones Linux.

Una vez hayamos instalado Epydoc siguiendo el método adecuado para nuestro sistema operativo tendremos acceso a su funcionalidad a través de dos interfaces de usuario distintas: el script epydoc, que consiste en una aplicación de línea de comandos, y el script epydocgui (epydoc.pyw en Windows), que ofrece una interfaz gráfica. Además también podemos acceder a la funcionalidad de epydoc programaticamente, como en el caso de pydoc.

Vamos a crear un pequeño módulo con un par de clases para ver primero el resultado de utilizar epydoc con docstrings de texto plano, sin ningún tipo de marcado especial.

"""Modulo para ejemplificar el uso de epydoc."""

class Persona:
    """Mi clase de ejemplo."""
    def __init__(self, nombre):
        """Inicializador de la clase Persona."""
        self.nombre = nombre
        self.mostrar_nombre()

    def mostrar_nombre(self):
        """Imprime el nombre de la persona"""
        print "Esta es la persona %s" % self.nombre

class Empleado(Persona):
    """Subclase de Persona."""
    pass


if __name__ == "__main__":
    raul = Persona("Raul")

El formato de salida por defecto de epydoc es HTML. Por lo tanto para generar la documentación en forma de documentos HTML bastaría escribir algo así:

epydoc ejemplo.py

o bien

epydoc –html ejemplo.py

Para generar un archivo PDF, utilizando LaTeX, se utilizaría el flag –pdf:

epydoc –pdf ejemplo.py

Si LaTeX no está instalado o epydoc no encuentra el ejecutable no será posible generar el PDF.

También podemos indicar el nombre del proyecto y la URL mediante las opciones –name y –url:

epydoc –name Ejemplo –url http://mundogeek.net ejemplo.py

E incluso añadir diagramas mostrando la clase base y subclases (–graph classtree), las llamadas entre funciones y métodos (–graph callgraph), clases y subclases usando notación UML (–graph umlclasstree) o todos ellos (–graph all).

epydoc –graph all ejemplo.py

Para generar el grafo de llamadas, no obstante, es necesario generar un archivo con la información necesaria utilizando el módulo profile o el módulo hotshot e indicar el archivo resultante utilizando el flag –pstat:

epydoc –graph all –pstat profile.out ejemplo.py

Veamos ahora algunas funcionalidades básicas de marcado en reStructuredText.

Para poner un texto en itálica se rodea el texto con asteriscos:

*itálica*   ->   itálica

Para ponerlo en negrita, se utilizan dos asteriscos:

**negrita**   ->   negrita

Para mostrar el texto como monoespacio, por ejemplo para mostrar código inline, se utiliza «.

«monoespacio«   ->   monoespacio

Si necesitamos utilizar cualquiera de estos caracteres especiales, se pueden escapar utilizando la barra invertida.

\* es un carácter especial   ->   * es un carácter especial

Los títulos se crean añadiendo una línea de caracteres no alfanuméricos por debajo del texto, o por encima y por debajo del texto. Para crear un subtitulo basta utilizar una nueva combinación.

Título
======

Subtitulo
———

Título

Subtitulo

Para crear una lista no ordenada se empieza cada línea con el caracter ‘*’, ‘-‘ o ‘+’:
* Python
* C
* Java

  • Python
  • C
  • Java

Para crear una lista numerada se empieza la línea con el número seguido de un punto, o bien con el símbolo ‘#’ para que se introduzca el número automáticamente.

1. Python
2. C
3. Java

  1. Python
  2. C
  3. Java

Para describir propiedades de los elementos que estamos documentando se utilizan los campos o fields. En reStructuredText los campos comienzan con ‘:’, le sigue el nombre del campo y opcionalmente sus argumentos, y se cierra de nuevo con ‘:’, para terminar con el cuerpo del campo.

Estos son algunos de los campos que soporta Epydoc:

Funciones y métodos
:param p: Un parámetro Describe el parámetro p.
:type p: str Especifica el tipo esperado para el parámetro p.
:return: True si son iguales Valor de retorno.
:rtype: str Tipo del valor de retorno.
:keyword p: Un parámetro Descripción del parámetro con valor por defecto y nombre p.
:raise e: Si el parámetro es cero Describe las circunstancias para las que se lanza la excepción e.

Variables
:ivar v: Una variable Descripción de la instancia v.
:cvar v: Una variable Descripción de la variable estática de clase v.
:var v: Una variable Descripción de la variable v del módulo.
:type v: str Tipo de la variable v.

Notas
:note: Una nota Una nota sobre el objeto.
:attention: Importante Una nota importante sobre el objeto.
:bug: No funciona para el valor 0 Descripción de un error en el objeto.
:warning: Cuidado con el valor 0 Una advertencia acerca de un objeto.
:see: Ver ‘Python para todos’ Para indicar información relacionada.

Estado
:version: 1.0 Versión actual del objeto.
:change: Versión inicial Listado de cambios.
:todo: Internacionalización Un cambio planeado para el objeto.
:status: Versión estable Estado del objeto.

Autoría
:author: Raul Gonzalez Autor o autores del objeto.
:organization: Mundo geek Organización que creó o mantiene el objeto.
:license: GPL Licencia del objeto.
:contact: zootropo en gmail Información de contacto del autor.

Para que Epydoc sepa que utilizamos reStructuredText es necesario indicarlo mediante una variable __docformat__ en el código, o bien mediante la opción –docformat de línea de comandos. Las opciones posibles son epytext, plaintext, restructuredtext o javadoc.

Veamos un ejemplo con campos:

"""Modulo para ejemplificar el uso de *epydoc*.
   :author: Raul Gonzalez
   :version: 0.1"""

__docformat__ = "restructuredtext"

class Persona:
    """Modela una persona."""
    def __init__(self, nombre, edad):
        """Inicializador de la clase `Persona`.
           :param nombre: Nombre de la persona.
           :param edad: Edad de la persona"""
        self.nombre = nombre
        self.edad = edad
        self.mostrar_nombre()

    def mostrar_nombre(self):
        """Imprime el nombre de la persona"""
        print "Esta es la persona %s" % self.nombre

class Empleado(Persona):
    """Subclase de `Persona` correspondiente a las personas
       que trabajan para la organizacion.
       :todo: Escribir implementacion."""
    pass


if __name__ == "__main__":
    juan = Persona("Juan", 26)

reStructuredText también soporta un segundo tipo de campos en el que el cuerpo del campo es una lista. De esta forma podemos, por ejemplo, describir todos los parámetros de una función o método con un solo campo :Parameters:, en lugar de con un campo :param: para cada parámetro.

class Persona:
    """Modela una persona."""
    def __init__(self, nombre, edad):
        """Inicializador de la clase `Persona`.

           :Parameters:
             - `nombre`: Nombre de la persona.
             - `edad`: Edad de la persona.
        """

        self.nombre = nombre
        self.edad = edad
        self.mostrar_nombre()

Otros campos que admiten listas son :Exceptions: para indicar varias excepciones (:except:), :Variables: para comentar varias variables (:var:) o :Ivariables: para comentar varias instancias (:ivar:).



16 comentarios en «Documentación en Python»

  1. Como siempre sublime 🙂

    En un curso de mi universidad estamos dando un cursillo basándonos en el tutorial que confeccionaste. Cuando lo termine lo complementaré con el resto de entradas dedicadas al python. Solo animarte a que sigas, si python ya es fácil, con tu blog ya está chupado 😀

    Evidentemente se te ha reconocido como autor tal y como dicta la licencia.

  2. La verdad que es exelente la página. Lei mucho por aqui pero es la primera vez que agradezco.
    Te felicito y te vuelvo a felicitar.
    Un abrazo y espero que sigas con todo esto!! 😀

  3. Buenas, eres una máquina!! Me encanta haberme «topado» con tu blog, porque escribes de una forma muy entendible para los que somos muy torpes de entendederas, y un favorcillo, me podrías recomendar un libro sobre python (obviamente he leido el tuyo, me encantó, pero ahora me gustaría ampliar conocimientos) y si hay alguno en castellano, mejor, aunque supongo que los buenos estarán en english. Un saludo, mucho ánimo y muchas gracias.

  4. Oye savedjuli la maquina soy yo, jajajaja,
    Como siempre Zootropo, como dicen en mi pais: Te la comiste. ja, felicidades, y pa’lante con PYTHON.

  5. The Machine, para mi que eres dominicano, jejeje

    Hey Zootropo, ahora mismo me pondré a leer tu manual y guardaré el link en Gmail (y despues en casa).

    Ahora mismo estoy leyendo y no practicando porque estoy en una maquina ajena (con Windows y muy lenta), aunque yo he corrido algunas aplicaciones hechas en python en una maquina menos de 64mb de ram (claro, con GNU/Linux tio :P)

    Gracias!!!

  6. Hola amigos, antes que nada para felicitarlos por el blog, muy buena informacion, felicidades.

    Aparte quiero hacerles una pregunta, espero que me puedan ayudar, el problema es el siguiente:

    Quiero ejecutar unos comandos del sistema operativo (linux) desde un script de Python, actualmente estoy utilizando ‘os.system(«comando»)’ y todo bien, pero necesito hacerlo con un cambio, necesito que el comando se ejecute en otra terminal o consola, no en la misma donde estoy ejecutando el script.

    Alguien me puede ayudar??

    Gracias
    Jerry

  7. Pingback: Documentando el código « Predesys

  8. Muchas gracias por este blog de verdad ayuda mucho a los que iniciamos en este mundo de Python.

    Solo una cosa, deberías agregar que para hacer uso de epydoc con reStructuredText se debe tener instalado (además del módulo Epydoc) el módulo Docutils de Python.

    Muchas Gracias!

  9. Excelente post.
    Tambien queria comentarles que esta apareciendo este mensaje a la entrada de su pagina—>

    Warning: parse_url(http://localhost:puerto) [function.parse-url]: Unable to parse URL in wp-content/plugins/jetpack/_inc/lib/class.media-extractor.php on line 20

Responder a Eurus Cancelar respuesta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.