domingo, 14 de abril de 2024

Documentación de un proyecto.

Basado en el proyecto de la RapsberryPi Org "Documenting your code".

Introducción.

Si has creado un proyecto realmente útil y quiere compartirlo con otra gente, un paso crucial es crear la documentación que ayude a los usuarios a entender que es lo que hace el código, como funciona y como poder usarlo.

Para compartir el código que hayas creado y ayudar a la gente a usarlo, crearemos un sitio web que contenga la documentación de tu proyecto.

Que haremos.

Esta guía te enseñará como construir un sitio web para tu proyecto que automáticamente cree la documentación par tu código Python.

documentación del programa de Python


Que aprenderás.

  • Como documentar el código de Python
  • Como automáticamente generar la documentación.
  • Como usar el reStructuredText markup language.
  • Como crear un proyecto web usando Sphinx.

Empecemos.


Imagina que has creado una obra maestra de software que hará la vida de innumerables programadores un poco más fácil y la has puesto a su disposición, pero nadie lo usa ¡Por que no saben como hacerlo!. Crear documentación es clave cuando compartes tu código.

En este post usaremos como ejemplo el código del proyecto "Mazo de Cartas" para documentarlo. No es esencial que hayas visto este proyecto, pero te sería útil porque te ayudaría a comprender que hace él código que vamos a documentar.

Básicamente el código del programa es el siguiente:

from random import shuffle

class Carta:
    def __init__(self, palo, numero):
        self._palo = palo
        self._numero = numero
    
    # getter y setter para el atributo palo
    @property
    def palo(self):
        return self._palo
    
    @palo.setter
    def palo(self, palo):
        if palo in ["oros", "copas", "espadas", "bastos"]:
            self._palo = palo
        else:
            print("¡Ese no es un palo de la baraja!")
            
    # getter y setter para el atributo numero
    @property
    def numero(self):
        return self._numero
    
    @numero.setter
    def numero(self, numero):
        if isinstance(numero, int):
            if numero in [1,2,3,4,5,6,7,10,11,12]:
                self._numero = numero
            else:
                print("El número de la carta debe estar entre 1 y 12")
                print("Excluyendo el 8 y el 9")
        else:
            print("El valor introducido debe ser númerico")
    
    def esta_en_mazo(self, mazo):
        """Comprueba si una determinada carta esta en el mazo que le
        pasamos o no"""
        for carta in mazo:
            if self.palo == carta.palo:
                if self.numero == carta.numero:
                    return True
        return False
        
            
            
    def __repr__(self):
        return str(self.numero) + " de " + self._palo 
            
class Mazo:    
    def __init__(self):
        self._cartas = []
        self.rellenar()
                
    # Creamos un getter
    @property
    def cartas(self):
        return self._cartas
        
    def rellenar(self):
        palos = ["oros", "copas", "espadas", "bastos"]
        numeros = [1,2,3,4,5,6,7,10,11,12]
        # Devuelve un mazo con las cartas en orden aleatorio.
        cartas = [Carta(p, n) for p in palos for n in numeros]
        shuffle(cartas)
        self._cartas = cartas
        
    def repartir(self, jugadores):
        manos = {jugador: Mano(jugador) for jugador in jugadores}
        for _ in range(3):  # Repartir 3 cartas a cada jugador
            for jugador in jugadores:
                carta = self._cartas.pop()
                manos[jugador].recibir_carta(carta)
        for mano in manos.values():
            mano.mostrar_mano()

class Mano:
    def __init__(self, jugador):
        self.jugador = jugador
        self.cartas = []

    def recibir_carta(self, carta):
        self.cartas.append(carta)

    def mostrar_mano(self):
        print(f"Mano de {self.jugador}:")
        for carta in self.cartas:
            print(carta)

        

mi_mazo = Mazo()
jugadores = ["Jugador 1", "Jugador 2"]
mi_mazo.repartir(jugadores)
Echa un vistazo al código. Verás que hay tres clases: Carta, Mazo y Mano. La clase "Carta" representa una única carta del juego, mientras que "Mazo" es una colección de cartas que primero se crean en un orden y luego pasan a tener uno aleatorio. La clase "Mano" se utiliza para repartir tres cartas a cada jugador.

Al final del programa se crea un mazo de la clase Mazo, se crean dos jugadores y se utiliza un método de la clase mazo para repartir tres cartas a cada uno.

Si ejecutas el programa verás algo parecido a esto:

>>> %Run carta.py
Mano de Jugador 1:
7 de copas
11 de copas
7 de espadas
Mano de Jugador 2:
11 de bastos
11 de espadas
1 de copas

Documentando el código.


Si le echas un vistazo al programa, te darás cuenta de que no hay información adicional que describa como funciona o como se utiliza.

El programa es pequeño, así que probablemente podrías revisar el código e inferir como funciona y que hace cada función. Pero ¿Que pasaría si el programa tuviese miles o millones de líneas de código?. Sería extremadamente difícil y llevaría muchísimo tiempo entender como funciona el programa sin algo de información adicional. 

Python te permite añadir información sobre un programa dentro del código usando docstrings.
Estos son la base para documentar el código. 

Los docstrings se ponen al comienzo del módulo, clase o función en forma de una cadena con tres comillas dobles a cada lado.

def holamundo():
    """ imprime 'hola mundo' en la pantalla """
    print("hola mundo")

Los Docstrings o cadenas de documentación, pueden ser una única línea (como en el ejemplo superior) o pueden ocupar varias.

def holamundo():
    """
    Esta función imprime "hola mundo" en la pantalla.
    No acepta argumentos, ni devuelve nada.
    """
    print("hola mundo")

Tu primera tarea será añadir una cadena de documentación a la clase "Carta" para describir la clase y para que sirve.

class Carta:
    """
    La clase carta representa una única carta y se
    inicializa pasándole el palo y número de la carta a
    representar.
    """
    def __init__(self, palo, numero):

        self._palo = palo
        self._numero = numero

Añade tú una posible cadena de documentación para las otras clases.


Generando la documentación.


Ahora que tu código contiene algo de información sobre si mismo en los formularios de los docstring, puedes usar el módulo de Python pydoc, para que automáticamente se cree un documento HTML con la documentación del código.

Abre una ventana de terminal (Raspberry Pi/Linux PC/Mac) o una ventana de comandos de Windows (Windows PC).

Navega hasta el directorio que contiene el programa carta.py.

cd nombre_del_directorio

Introduce el comando para que el módulo pydoc cree la documentación.

WINDOWS.

python -m pydoc -w .\card.py
Si obtienes un error al ejecutar este comando, consulta la guía uso de Pip en Windows para obtener ayuda con la instalación y asegúrate de haber agregado Python a su ruta.

Raspberry Pi/Linux PC/Mac
python3 -m pydoc -w ./card.py


Cuando el comando ha terminado de ejecutarse, saldrá el mensaje ‘wrote carta.html’. Date cuenta que pydoc usa el nombre del programa de Python como nombre para el archivo HTML.

Abre el archivo carta.html usando tu navegador para ver la documentación que se ha creado.

Verás una página que muestra las clases Carta, Mano y Mazo. Para sus métodos y propiedades, incluyendo la documentación que has añadido como cadenas de documentación.


documentación generada por pydoc


Puedes utilizar esta página HTML sencilla en un servidor Web para proporcionar a tus usuarios información sobre tu código. En los siguientes pasos veremos como usar la herramienta Sphinx para crear un sitio web de documentación al que puedas añadir contenido e información adicional. 


Creando un proyecto web con Sphinx.

Usar pydoc y docstrings es una excelente manera de crear documentación estructurada sobre tu código, pero tiene limitaciones porque no puedes agregar información adicional y contenido.

En un sitio web menos básico, similar al de GPIO Zero, por ejemplo, puedes agregar mucha más información además de la documentación, preguntas frecuentes, imágenes y fragmentos de código.

Usaremos Sphinx para crear un proyecto de sitio web de este tipo. Esta herramienta fue creada originalmente para documentar el lenguaje Python.

Para crear contenido para tu sitio web con Sphinx, escribirás archivos de texto formateados utilizando el lenguaje de marcado simple pero potente reStructuredText (ReST).


Instalar Sphinx.

Usa pip3 para instalar este módulo, por ejemplo:

pip3 install sphinx


Crea un proyecto con Sphinx.


Sphinx incluye una utilidad para crear de forma rápida una plantilla base.

  • Navega al directorio de tu proyecto.

  • Ejecuta el siguiente comando:
sphinx-quickstart




Usa las siguientes respuestas para completar el cuestionario que te realizará la instalación y que determinará las propiedades y la configuración que Sphinx usará para crear la página web.

Son unas pocas preguntas - puedes usar las siguientes respuestas:

Pregunta                                                        Por defecto        Respuesta

- Separar directorios fuente y compilado        n                          n
- Nombre del proyecto                                    n                          carta
- Autor                                                             n                          (nombre autor)
- Liberación del proyecto                                n                          (versión proyecto)
- Lenguaje del proyecto                                  en                         es

El programa de instalación rápida de Sphinx creará unos cuantos archivos y directorios donde se creará tu documentación. Por ejemplo, en Linux se crean los siguientes archivos:

archivos creados por sphinx en linux

Los principales archivos y directorios son:

conf.py - El archivo de configuración de Sphinx que describe como se creará tu documentación. 

index.rst - La página principal e índice de tu documentación.   

_build - El directorio donde se creará la documentación.

Construyendo la página Web.

Para ver como va el proyecto, lo primero que tenemos que hacer es crearlo. Esto lo que hará será convertir los ficheros del proyecto en archivos HTML. 

Ejecuta el siguiente comando para ello:

make html


instalación de sphinx-quickstar


Esto creará un directorio llamado html dentro del directorio _build. Ahí se crearán los archivos html del proyecto. 

Abre el archivo index.html que esta dentro del directorio _build/html usando un navegador web. Verás la página inicial del proyecto, aun sin construir. 

sphinx, inicio html

Lo próximo que haremos será añadir el contenido al proyecto, incluyendo la auto-documentación que antes creamos para el código con pydoc.

Añadiendo la documentación del código.


Configura el archivo conf.py

Para incluir el código de la documentación que creamos antes, Sphinx necesita conocer donde encontrarlo. Se lo diremos modificando el archivo conf.py

Abre el archivo conf.py,  y al comienzo del programa incluye el siguiente código:

# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

import os
import sys
sys.path.insert(0, os.path.abspath('.'))


# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = ['sphinx.ext.autodoc']

Esto añade el actual directorio a la configuración de Sphinx para que pueda encontrar los archivos de código que necesita.

Añadiendo la documentación de tu código.

En ese mismo directorio crea un archivo llamado code.rst. Aquí estará el código para tu página de documentación. 

Añade el título a esta página.

Documentación del Código
=========================


Añade también el siguiente código para importar el módulo carta.py

.. module:: carta

A continuación añade lo siguiente para añadir la documentación para las clases Carta, Mano y Mazo.

.. autoclass:: Carta
    :members:
.. autoclass:: Mano
    :members:
.. autoclass:: Mazo
    :members:

Este archivo debe quedar como este:

Documentación del Código
=========================
.. module:: carta
.. autoclass:: Carta
    :members:
.. autoclass:: Mano
    :members:
.. autoclass:: Mazo
    :members:


Para que lo que hemos especificado en este archivo, code.rst aparezca en la página web de tu proyecto, tenemos que añadirlo al índice, para lo cual tienes que abrir el archivo index.rst y modificarlo, añadiendo la página "code" justo debajo de en la tabla de contenidos ..toctree:: - El resultado debería ser algo como esto:

¡Bienvenido a la documentación de carta!
==========================================

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   code
Vuelve a reconstruir el proyecto usando el comando:

make html 

Con lo que la página del código aparecerá ahora en el índice. Para ver si todo funciona correctamente vuelve a abrir el directorio _build/html y abre de nuevo el archivo index.html en el navegador. 

La página "code" que hemos creado aparecerá ahora en el índice.


pagina de inicio con el código añadido


Cuando abres el enlace "documentación del código" verás la documentación autogenerada para cada una de las clases del módulo.

documentación de las clases del módulo


Creando páginas.


Además de las páginas que describen el código del programa, puedes incluir otras con otro contenido dentro del proyecto web.

Todas las páginas que quieras añadir tienen que estar formateadas usando el lenguaje Rest markup, Este enlace te mostrará como usar las características más comunes de este lenguaje. 

Para empezar crearemos una página "acerca de" que mostrará algunos detalles del proyecto y donde encontrar más información.

Crea un nuevo archivo en el mismo directorio llamado acerca_de.rst.

Luego comenzaremos añadiendo el título de la nueva página.

Sobre este Proyecto.
==================

Luego añadiremos algo de texto que describa el proyecto, por ejemplo:

Mazo de cartas es un conjunto de clases para crear naipes de una baraja. 

Quizá será bueno añadir un enlace al código fuente del proyecto:

Puedes crear este proyecto tu mismo visitando "esta página <https://projects.raspberrypi.org/en/projects/deck-of-cards>"_.


Información: URLs in ReST

La estructura de una URL en lenguaje markup es muy específica en ReST.
El texto del link y el enlace mismo necesitan estar entre dos guiones
simples, el segundo de los cuales tiene que estar seguido por un guion 
bajo, de esta forma:
'texto_del_link <url>'_

Es importante dejar un espacion en blanco entre el texto del link y su 
enlace.

Este tipo de marcado se denomina en línea porque está dentro del texto.

También puede crear definiciones de URL independientes y luego hacer 
referencia a ellas en cualquier parte del texto; esto es útil si desea 
vincular a la misma URL dos veces.

Por favor 'raspberry pi'_.

.. _raspberry pi: https://raspberrypi.org/

Cuando hayas terminado de crear la página "acerca_de", añadela a la pagina del índice "index.rst"

¡Bienvenido a la documentación de carta!
==========================================

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   code
   acerca_de 

Reconstruye el proyecto de nuevo con la instrucción:

make html

página acerca de

Como reto personal puedes añadir nuevas páginas al proyecto como por ejemplo una de preguntas frecuentes.


Desafío: Mejorar la página Web

  1. Investiga las diferentes opciones que Sphinx no facilita para personalizar nuestro proyecto, como por ejemplo el cambio de tema
  2. Puedes documentar mejor el programa añadiendo los docstring que consideres oportuno.
  3. Aloja tu proyecto en Github o en Read the Docs

Puede encontrar el código de este post en este enlace de Github.

No hay comentarios:

Publicar un comentario