La librería estándar de Python#
El lenguaje Python proporciona una extensísima librería de módulos que nos sirven para construir nuestro software sin tener que reimplementar una y otra vez algunas funcionalidades que suelen ser comunes a todo tipo de programas.
Entre esas librerías podemos mencionar algunas de las más comunes:
os
incluye muchas funciones y variables útiles para interactuar con aspectos específicos del sistema operativo y el entorno de ejecución: variables de entorno, rutas de ficheros, descriptores de ficheros de entrada, salida y error estándar…sys
nos permite acceder a la línea de comandos de nuestro programa (sys.argv
) y también a la funciónsys.exit
que nos permite terminar la ejecución e indicar el código de salida.re
incluye todas las funcionalidades relativas a expresiones regulares.socket
contiene toda la librería que da acceso de bajo nivel a sockets de comunicación.math
incluye una buena colección de funciones matemáticas y valores para constantes.
Esto es sólo una pequeña muestra de toda la librería estándar de Python.
En este apartado vamos a ver un par de librerías que deberéis utilizar en
vuestra práctica y que os facilitarán la vida: argparse
y logging
argparse
#
Casi todos los programas que se ejecutan desde la línea de comandos suelen
necesitar leer una serie de parámetros y argumentos de la misma para
decidir qué acciones deben realizarse. A través de la librería sys
podemos acceder fácilmente a la línea que ha sido ejecutada, usando la
variable sys.argv
.
Aunque en algunas ocasiones es posible que con lo anterior sea suficiente, en otras muchas no lo será. Además, también es recomendable proporcionar una ayuda cuando se solicite o se comenta algún error en la entrada, e implementar todo eso a mano suele ser bastante tedioso.
Para ello, Python proporciona la librería argparse
. Básicamente esta
librería funciona definiendo un objeto ArgumentParser
al cuál le vamos
añadiendo los diferentes argumentos que queremos que soporte nuestro
programa. Cada uno de estos argumentos están ligados una opción en la línea
de comandos, unos argumentos y puede definir un texto de ayuda que permita
generar la salida del comando de ayuda de forma automática.
#!/usr/bin/env python3
import argparse
def parse_commandline():
parser = argparse.ArgumentParser(description="Commandline calculator")
parser.add_argument('value1', type=float, help='First number')
parser.add_argument('value2', type=float, help='Second number')
return parser.parse_args()
def main():
user_options = parse_commandline()
print(user_options.value1 + user_options.value2)
if __name__ == '__main__':
main()
En el ejemplo anterior se puede observar que nuestro entrypoint va a
ejecutar primero la función parse_commandline
. En dicha función se crea
el objeto argparse.ArgumentParser
y se añaden dos argumentos a la línea
de órdenes. El valor devuelto es el resultado de la llamada a parse_args
,
que analizará la línea de argumentos que ha recibido nuestro programa y
devolverá, si es correcta, un Namespace
que contendrá los valores
recibidos. En este caso, el programa no debe recibir ninguna opción y
aceptará obligatoriamente 2 valores de tipo float
. Si se pasaran menos o
más de 2, automáticamente el programa fallaría.
#!/usr/bin/env python3
import argparse
import sys
_ALLOWED_OPERATIONS_ = ['sum', 'sub', 'mul', 'div']
def parse_commandline():
parser = argparse.ArgumentParser(description="Commandline calculator")
parser.add_argument('value1', type=float, help='First number')
parser.add_argument('value2', type=float, help='Second number')
parser.add_argument(
'-o',
'--operation',
choices=_ALLOWED_OPERATIONS_,
default='sum',
dest='op',
help='Operation to execute',
)
return parser.parse_args()
def main():
user_options = parse_commandline()
if user_options.op == 'sum':
print(user_options.value1 + user_options.value2)
elif user_options.op == 'sub':
print(user_options.value1 - user_options.value2)
elif user_options.op == 'mul':
print(user_options.value1 * user_options.value2)
elif user_options.op == 'div':
print(user_options.value1 / user_options.value2)
if __name__ == '__main__':
main()
sys.exit(0)
En éste otro ejemplo se ha añadido un argumento de opción (-o
o
--operation
) que permite pasar un valor dentro de una lista de valores
permitidos y que hace que nuestro método main
se comporte de manera
diferente según el valor especificado.
logging
#
En todo proyecto suele ser necesario añadir diferentes mensajes que indiquen qué está haciendo nuestro programa, tanto a nivel de tener informes de lo que está sucediendo como, en tiempo de desarrollo, para depurar el funcionamiento del programa.
Todos los lenguajes permiten escribir mensajes en el terminal de modo que podamos ver dichos mensajes y saber qué está ocurriendo, pero esa es una solución muy poco práctica, ya que en ocasiones podemos olvidarnos de quitar mensajes de depuración y terminan llegando al producto final, o que muestran información que no debería ser vista por los usuarios de nuestra aplicación.
En ocasiones también es muy práctico poder volcar, según el tipo o la
gravedad, los mensajes a la salida estándar, de error o a un fichero, o
incluso yendo más allá, utilizar algún sistema de monitorización de logs en
la nube. Evidentemente, para este tipo de situaciones un simple print
se
nos queda muy corto.
Python proporciona la librería logging
que permite toda esta serie
de operaciones:
Definir diferentes “loggers”, que tengan diferentes configuraciones cada uno de ellos.
Cada mensaje puede ser enviado con un nivel de “severidad” diferente, yendo desde mensajes de depuración (debug) a mensajes criticos (critical).
Desacoplar la escritura de cada mensaje de su configuración, no teniendo que preocuparnos en nuestro código de si el mensaje va a ser enviado a la salida estándar, fichero o un servicio en la nube remoto.
Desacoplar el formato con el que se mostrará o almacenará el mensaje de la escritura del mismo.
Veamos algunos ejemplos:
import logging
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")
El ejemplo anterior muestra el uso más básico de la librería, ya que todos los mensajes se enviarán al logger denominado “root”, al no haber especificado otro. Cada mensaje se envía con un nivel diferente. Si lo ejecutamos, veremos que sólo se imprimirán por la salida de error estándar los mensajes de nivel “warning”, “error” y “critical”, ya que el logger por defecto está configurado para emitir a su salida únicamente los mensajes de nivel superior a “info”.
Además, se imprimen con un formato muy concreto:
$ python3 log_example.py
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message
Ese formato por defecto utilizado incluye en primer lugar el nivel, luego el nombre del logger y por último el mensaje.
Para configurar la salida podemos utilizar la siguiente función:
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s]:%(message)s', force=True)
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")
En este caso, estamos proporcionando una configuración por defecto diferente, especificando que queremos imprimir aquellos mensajes de nivel “info” o superior y modificando el formato del mensaje. Si volvemos a ejecutarlo ahora, obtendremos:
$ python3 log_example.py
2022-09-25 14:09:42,317 [INFO]:This is an info message
2022-09-25 14:09:42,317 [WARNING]:This is a warning message
2022-09-25 14:09:42,317 [ERROR]:This is an error message
2022-09-25 14:09:42,317 [CRITICAL]:This is a critical message
Como se puede observar, con sólo una línea se ha modificado por completo la
salida del programa sin tener que modificar el código como tal de la
emisión de mensajes, mostrando el enorme desacoplamiento entre
configuración y mensajes que permite la librería logging
.
Para más información sobre el uso de esta librería, Python proporciona unos tutoriales básico y avanzado en su documentación.