10. Pequeño paseo por la Biblioteca Estándar
********************************************


10.1. Interfaz al sistema operativo
===================================

El módulo "os" provee docenas de funciones para interactuar con el
sistema operativo:

   >>> import os
   >>> os.getcwd()      # Return the current working directory
   'C:\\Python311'
   >>> os.chdir('/server/accesslogs')   # Change current working directory
   >>> os.system('mkdir today')   # Run the command mkdir in the system shell
   0

Asegúrate de usar el estilo "import os" en lugar de "from os import
*". Esto evitará que "os.open()" oculte a la función integrada
"open()", que trabaja bastante diferente.

Las funciones integradas "dir()" y "help()" son útiles como ayudas
interactivas para trabajar con módulos grandes como "os":

   >>> import os
   >>> dir(os)
   <returns a list of all module functions>
   >>> help(os)
   <returns an extensive manual page created from the module's docstrings>

Para tareas diarias de administración de archivos y directorios, el
módulo "shutil" provee una interfaz de más alto nivel que es más fácil
de usar:

   >>> import shutil
   >>> shutil.copyfile('data.db', 'archive.db')
   'archive.db'
   >>> shutil.move('/build/executables', 'installdir')
   'installdir'


10.2. Comodines de archivos
===========================

El módulo "glob" provee una función para hacer listas de archivos a
partir de búsquedas con comodines en directorios:

   >>> import glob
   >>> glob.glob('*.py')
   ['primes.py', 'random.py', 'quote.py']


10.3. Argumentos de linea de órdenes
====================================

Los programas frecuentemente necesitan procesar argumentos de linea de
órdenes. Estos argumentos se almacenan en el atributo *argv* del
módulo "sys" como una lista.  Por ejemplo, la siguiente salida resulta
de ejecutar "python demo.py uno dos tres" en la línea de órdenes:

   >>> import sys
   >>> print(sys.argv)
   ['demo.py', 'one', 'two', 'three']

El modulo "argparse" provee un mecanismo más sofisticado para procesar
argumentos recibidos vía línea de comandos. El siguiente *script*
extrae uno o más nombres de archivos y un número opcional de líneas
para mostrar:

   import argparse

   parser = argparse.ArgumentParser(
       prog='top',
       description='Show top lines from each file')
   parser.add_argument('filenames', nargs='+')
   parser.add_argument('-l', '--lines', type=int, default=10)
   args = parser.parse_args()
   print(args)

Cuando se ejecuta por línea de comandos haciendo "python top.py
--lines=5 alpha.txt beta.txt", el *script* establece "args.lines" a
"5" y "args.filenames" a "['alpha.txt', 'beta.txt']".


10.4. Redirigir la salida de error y finalización del programa
==============================================================

El módulo "sys" también tiene atributos para *stdin*, *stdout*, y
*stderr*.  Este último es útil para emitir mensajes de alerta y error
para que se vean incluso cuando se haya redirigido *stdout*:

   >>> sys.stderr.write('Warning, log file not found starting a new one\n')
   Warning, log file not found starting a new one

La forma más directa de terminar un programa es usar "sys.exit()".


10.5. Coincidencia en patrones de cadenas
=========================================

El módulo "re" provee herramientas de expresiones regulares para un
procesamiento avanzado de cadenas.  Para manipulación y coincidencias
complejas, las expresiones regulares ofrecen soluciones concisas y
optimizadas:

   >>> import re
   >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
   ['foot', 'fell', 'fastest']
   >>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
   'cat in the hat'

Cuando se necesita algo más sencillo solamente, se prefieren los
métodos de las cadenas porque son más fáciles de leer y depurar:

   >>> 'tea for too'.replace('too', 'two')
   'tea for two'


10.6. Matemática
================

El módulo "math" permite el acceso a las funciones de la biblioteca C
subyacente para la matemática de punto flotante:

   >>> import math
   >>> math.cos(math.pi / 4)
   0.70710678118654757
   >>> math.log(1024, 2)
   10.0

El módulo "random" provee herramientas para realizar selecciones al
azar:

   >>> import random
   >>> random.choice(['apple', 'pear', 'banana'])
   'apple'
   >>> random.sample(range(100), 10)   # sampling without replacement
   [30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
   >>> random.random()    # random float
   0.17970987693706186
   >>> random.randrange(6)    # random integer chosen from range(6)
   4

El módulo "statistics" calcula propiedades de estadística básica (la
media, mediana, varianza, etc) de datos numéricos:

   >>> import statistics
   >>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
   >>> statistics.mean(data)
   1.6071428571428572
   >>> statistics.median(data)
   1.25
   >>> statistics.variance(data)
   1.3720238095238095

El proyecto SciPy <https://scipy.org> tiene muchos otros módulos para
cálculos numéricos.


10.7. Acceso a Internet
=======================

Hay varios módulos para acceder a Internet y procesar sus protocolos.
Dos de los más simples son "urllib.request" para traer data de URLs y
"smtplib" para mandar correos:

   >>> from urllib.request import urlopen
   >>> with urlopen('http://worldtimeapi.org/api/timezone/etc/UTC.txt') as response:
   ...     for line in response:
   ...         line = line.decode()             # Convert bytes to a str
   ...         if line.startswith('datetime'):
   ...             print(line.rstrip())         # Remove trailing newline
   ...
   datetime: 2022-01-01T01:36:47.689215+00:00

   >>> import smtplib
   >>> server = smtplib.SMTP('localhost')
   >>> server.sendmail('soothsayer@example.org', 'jcaesar@example.org',
   ... """To: jcaesar@example.org
   ... From: soothsayer@example.org
   ...
   ... Beware the Ides of March.
   ... """)
   >>> server.quit()

(Notá que el segundo ejemplo necesita un servidor de correo corriendo
en la máquina local)


10.8. Fechas y tiempos
======================

El módulo "datetime" ofrece clases para gestionar fechas y tiempos
tanto de manera simple como compleja.  Aunque soporta aritmética sobre
fechas y tiempos, el foco de la implementación es en la extracción
eficiente de partes para gestionarlas o formatear la salida.  El
módulo también soporta objetos que son conscientes de la zona horaria.

   >>> # dates are easily constructed and formatted
   >>> from datetime import date
   >>> now = date.today()
   >>> now
   datetime.date(2003, 12, 2)
   >>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
   '12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.'

   >>> # dates support calendar arithmetic
   >>> birthday = date(1964, 7, 31)
   >>> age = now - birthday
   >>> age.days
   14368


10.9. Compresión de datos
=========================

Los formatos para archivar y comprimir datos se soportan directamente
con los módulos: "zlib", "gzip", "bz2", "lzma", "zipfile" y "tarfile".

   >>> import zlib
   >>> s = b'witch which has which witches wrist watch'
   >>> len(s)
   41
   >>> t = zlib.compress(s)
   >>> len(t)
   37
   >>> zlib.decompress(t)
   b'witch which has which witches wrist watch'
   >>> zlib.crc32(s)
   226805979


10.10. Medición de rendimiento
==============================

Algunos usuarios de Python desarrollan un profundo interés en saber el
rendimiento relativo de las diferentes soluciones al mismo problema.
Python provee una herramienta de medición que responde esas preguntas
inmediatamente.

Por ejemplo, puede ser tentador usar la característica de empaquetado
y desempaquetado de las tuplas en lugar de la solución tradicional
para intercambiar argumentos.  El módulo "timeit" muestra rápidamente
una modesta ventaja de rendimiento:

   >>> from timeit import Timer
   >>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
   0.57535828626024577
   >>> Timer('a,b = b,a', 'a=1; b=2').timeit()
   0.54962537085770791

En contraste con el fino nivel de medición del módulo "timeit", los
módulos "profile" y "pstats" proveen herramientas para identificar
secciones críticas de tiempo en bloques de código más grandes.


10.11. Control de calidad
=========================

Una forma para desarrollar software de alta calidad es escribir
pruebas para cada función mientras se la desarrolla, y correr esas
pruebas frecuentemente durante el proceso de desarrollo.

El módulo "doctest" provee una herramienta para revisar un módulo y
validar las pruebas integradas en las cadenas de documentación (o
*docstring*) del programa.  La construcción de las pruebas es tan
sencillo como cortar y pegar una ejecución típica junto con sus
resultados en los docstrings.  Esto mejora la documentación al proveer
al usuario un ejemplo y permite que el módulo *doctest* se asegure que
el código permanece fiel a la documentación:

   def average(values):
       """Computes the arithmetic mean of a list of numbers.

       >>> print(average([20, 30, 70]))
       40.0
       """
       return sum(values) / len(values)

   import doctest
   doctest.testmod()   # automatically validate the embedded tests

El módulo "unittest" necesita más esfuerzo que el módulo "doctest",
pero permite que se mantenga en un archivo separado un conjunto más
comprensivo de pruebas:

   import unittest

   class TestStatisticalFunctions(unittest.TestCase):

       def test_average(self):
           self.assertEqual(average([20, 30, 70]), 40.0)
           self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
           with self.assertRaises(ZeroDivisionError):
               average([])
           with self.assertRaises(TypeError):
               average(20, 30, 70)

   unittest.main()  # Calling from the command line invokes all tests


10.12. Las pilas incluidas
==========================

Python tiene una filosofía de "pilas incluidas".  Esto se ve mejor en
las capacidades robustas y sofisticadas de sus paquetes más grandes.
Por ejemplo:

* The "xmlrpc.client" and "xmlrpc.server" modules make implementing
  remote procedure calls into an almost trivial task.  Despite the
  modules' names, no direct knowledge or handling of XML is needed.

* El paquete "email" es una biblioteca para administrar mensajes de
  correo electrónico, incluidos MIME y otros documentos de mensajes
  basados en **RFC 2822**. A diferencia de "smtplib" y "poplib" que
  realmente envían y reciben mensajes, el paquete de correo
  electrónico tiene un conjunto de herramientas completo para crear o
  decodificar estructuras de mensajes complejas (incluidos los
  archivos adjuntos) y para implementar protocolos de codificación y
  encabezado de Internet.

* El paquete "json" proporciona un sólido soporte para analizar este
  popular formato de intercambio de datos.  El módulo "csv" admite la
  lectura y escritura directa de archivos en formato de valor separado
  por comas, comúnmente compatible con bases de datos y hojas de
  cálculo.  El procesamiento XML es compatible con los paquetes
  "xml.etree.ElementTree", "xml.dom" y "xml.sax". Juntos, estos
  módulos y paquetes simplifican en gran medida el intercambio de
  datos entre aplicaciones de Python y otras herramientas.

* El módulo "sqlite3" es un *wrapper* para la biblioteca de bases de
  datos SQLite, proporcionando una base de datos persistente que se
  puede actualizar y acceder mediante una sintaxis SQL ligeramente no
  estándar.

* La internacionalización es compatible con una serie de módulos,
  incluyendo "gettext", "locale", y el paquete "codecs".
