10. Survol de la bibliothèque standard
**************************************


10.1. Interface avec le système d'exploitation
==============================================

Le module "os" propose des dizaines de fonctions pour interagir avec
le système d'exploitation :

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

Veillez bien à utiliser "import os" plutôt que "from os import *",
sinon "os.open()" cache la primitive "open()" qui fonctionne
différemment.

Les primitives "dir()" et "help()" sont des aides utiles lorsque vous
travaillez en mode interactif avez des gros modules comme "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>

Pour la gestion des fichiers et dossiers, le module "shutil" expose
une interface plus abstraite et plus facile à utiliser :

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


10.2. Jokers sur les noms de fichiers
=====================================

Le module "glob" fournit une fonction pour construire des listes de
fichiers à partir de motifs :

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


10.3. Paramètres passés en ligne de commande
============================================

Typiquement, les outils en ligne de commande ont besoin de lire les
paramètres qui leur sont donnés. Ces paramètres sont stockés dans la
variable "argv" du module "sys" sous la forme d'une liste. Par
exemple, l'affichage suivant vient de l'exécution de "python demo.py
one two three" depuis la ligne de commande :

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

Le module "argparse" fournit un mécanisme plus sophistiqué pour
traiter les arguments de la ligne de commande. Le script suivant
extrait un ou plusieurs noms de fichiers et un nombre facultatif de
lignes à afficher :

   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)

Lorsqu'il est exécuté avec la ligne de commande "python top.py
--lines=5 alpha.txt beta.txt", le script définit "args.lines" à "5" et
"args.filenames" à "['alpha.txt', 'beta.txt']".


10.4. Redirection de la sortie d'erreur et fin d'exécution
==========================================================

Le module "sys" a aussi des attributs pour *stdin*, *stdout* et
*stderr*. Ce dernier est utile pour émettre des messages
d'avertissement ou d'erreur qui restent visibles même si *stdout* est
redirigé :

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

Le moyen le plus direct de terminer un script est d'utiliser
"sys.exit()".


10.5. Recherche de motifs dans les chaînes
==========================================

Le module "re" fournit des outils basés sur les expressions
rationnelles permettant des opérations complexes sur les chaînes.
C'est une solution optimisée, utilisant une syntaxe concise, pour
rechercher des motifs complexes ou effectuer des remplacements
complexes dans les chaînes :

   >>> 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'

Lorsque les opérations sont simples, il est préférable d'utiliser les
méthodes des chaînes. Elles sont plus lisibles et plus faciles à
déboguer :

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


10.6. Mathématiques
===================

Le module "math" donne accès aux fonctions sur les nombres à virgule
flottante (*float* en anglais) de la bibliothèque C :

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

Le module "random" offre des outils pour faire des tirages aléatoires
:

   >>> 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

Le module "statistics" permet de calculer des valeurs statistiques
basiques (moyenne, médiane, variance…) :

   >>> 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

Le projet SciPy <https://scipy.org> contient beaucoup d'autres modules
dédiés aux calculs numériques.


10.7. Accès à internet
======================

Il existe beaucoup de modules permettant d'accéder à internet et gérer
les protocoles réseaux. Les deux plus simples sont "urllib.request"
qui permet de récupérer des données à partir d'une URL et "smtplib"
pour envoyer des courriers électroniques :

   >>> 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()

(Notez que le deuxième exemple a besoin d'un serveur mail tournant
localement.)


10.8. Dates et heures
=====================

Le module "datetime" propose des classes pour manipuler les dates et
les heures de manière simple ou plus complexe. Bien que faire des
calculs de dates et d'heures soit possible, la priorité de
l'implémentation est mise sur l'extraction efficace des attributs pour
le formatage et la manipulation. Le module gère aussi les objets
dépendant des fuseaux horaires

   >>> # 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. Compression de données
============================

Les formats d'archivage et de compression les plus communs sont
directement gérés par les modules "zlib", "gzip", "bz2", "lzma",
"zipfile" et "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. Mesure des performances
==============================

Certains utilisateurs de Python sont très intéressés par les
performances de différentes approches d'un même problème. Python
propose un outil de mesure répondant simplement à ces questions.

Par exemple, pour échanger deux variables, il peut être tentant
d'utiliser l'empaquetage et le dépaquetage de *n*-uplets plutôt que la
méthode traditionnelle. Le module "timeit" montre rapidement le léger
gain de performance obtenu :

   >>> 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 opposition à "timeit" et sa granularité fine, "profile" et "pstats"
fournissent des outils permettant d'identifier les parties les plus
gourmandes en temps d'exécution dans des volumes de code plus grands.


10.11. Contrôle qualité
=======================

Une approche possible pour développer des applications de très bonne
qualité est d'écrire des tests pour chaque fonction au fur et à mesure
de son développement, puis d'exécuter ces tests fréquemment lors du
processus de développement.

Le module "doctest" cherche des tests dans les chaînes de
documentation. Un test ressemble à un simple copier-coller d'un appel
et son résultat depuis le mode interactif. Cela améliore la
documentation en fournissant des exemples tout en prouvant qu'ils sont
justes :

   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

Le module "unittest" requiert plus d'efforts que le module "doctest"
mais il permet de construire un jeu de tests plus complet que l'on
fait évoluer dans un fichier séparé :

   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. Piles fournies
=====================

Python adopte le principe des "piles fournies". Vous pouvez le
constater au travers des fonctionnalités évoluées et solides fournies
par ses plus gros paquets. Par exemple :

* Les modules "xmlrpc.client" et "xmlrpc.server" permettent d'appeler
  des fonctions à distance quasiment sans effort. En dépit du nom des
  modules, aucune connaissance du XML n'est nécessaire.

* Le paquet "email" est une bibliothèque pour gérer les messages
  électroniques, incluant les MIME et autres encodages basés sur la
  **RFC 2822**. Contrairement à "smtplib" et "poplib" qui envoient et
  reçoivent des messages, le paquet *email* est une boite à outils
  pour construire, lire des structures de messages complexes
  (comprenant des pièces jointes) ou implémenter des encodages et
  protocoles.

* Le paquet "json" permet de lire et d'écrire du JSON, format
  d'encodage de données répandu. Le module "csv" gère la lecture et
  l'écriture de données stockées sous forme de valeurs séparées par
  des virgules dans des fichiers (*Comma-Separated Values* en
  anglais), format typiquement interopérable avec les bases de données
  et les feuilles de calculs. Pour la lecture du XML, utilisez les
  paquets "xml.etree.ElementTree", "xml.dom" et "xml.sax". Combinés,
  ces modules et paquets simplifient grandement l'échange de données
  entre les applications Python et les autres outils.

* Le module "sqlite3" est une abstraction de la bibliothèque SQLite,
  permettant de manipuler une base de données persistante, accédée et
  manipulée en utilisant une syntaxe SQL quasi standard.

* L'internationalisation est possible grâce à de nombreux paquets tels
  que "gettext", "locale" ou "codecs".
