📂 Python Input/Output#

Python et les fichiers#

La fonction open() renvoie un objet de type file et est le plus souvent utilisée avec deux arguments : open(nomfichier, mode).

workfile = open('workfile', 'w')

C’est une bonne pratique d’utiliser le mot-clĂ© with lorsque vous traitez des fichiers. Vous fermez ainsi toujours correctement le fichier, mĂȘme si une exception est levĂ©e. Utiliser with est aussi beaucoup plus court que d’utiliser l’équivalent avec des blocs try-finally:

Lecture d’un fichier#
with open('workfile') as workfile:
    read_data = workfile.read() # read all file content as a string
    lines = workfile.readlines() # read all lines and store them in a list
Ecriture d’un fichier#
with open('writefile', 'w') as writefile:
    writefile.write('Hello World')

Avertissement

Si vous n’utilisez pas le mot clef with, vous devez appeler workfile.close() pour fermer le fichier et immĂ©diatement libĂ©rer les ressources systĂšme qu’il utilise.

Indication

Les modules os et os.path permettent de manipuler facilement des fichiers.

import os
os.remove('workfile') # Delete workfile
os.path.exists('workfile') # Return True if workfile exist else False
os.path.getsize('workfile') # Return file size
os.path.isfile('workfile') # Return True if workfile is a file

Fichier CSV et Python#

Comma-separated values

Comma-separated values, connu sous le sigle CSV, est un format texte ouvert reprĂ©sentant des donnĂ©es tabulaires sous forme de valeurs sĂ©parĂ©es par des virgules. Le format fut utilisĂ© pendant des annĂ©es avant qu’aient lieu des tentatives de standardisation avec la RFC 4180. L’absence de format bien dĂ©fini signifie que des diffĂ©rences subtiles existent dans la production et la consommation de donnĂ©es par diffĂ©rentes applications. Ces diffĂ©rences peuvent gĂȘner lors du traitement de fichiers CSV depuis des sources multiples.

Fichier au format .csv#
ref.,name,price,release-date
IEL-51619,Ishtar - Les Jardins De Babylone,40.0,2019-10-04
BLU-KIN01,Kingdomino,19.90,2016-10-21
BO-DRA01,Draftosaurus,19.90,2019-02-22
FLY004JU,Jurassic Snack,19.90,2018-04-06
Représentation du fichier sous forme tabulaire#

ref.

name

price

release-date

IEL-51619

Ishtar - Les Jardins De Babylone

40.0

2019-10-04

BLU-KIN01

Kingdomino

19.90

2016-10-21

BO-DRA01

Draftosaurus

19.90

2019-02-22

FLY004JU

Jurassic Snack

19.90

2018-04-06

Note

Le dĂ©limiteur de colonnes peut ĂȘtre autre chose qu’une virgule, il arrive frĂ©quemment d’utiliser le point-virgule plutĂŽt que la virgule comme dĂ©limiteur.

Le module csv fournit des classes pour lire et écrire des données tabulaires au format CSV.

Exemple de lecture#
import csv
with open('eggs.csv') as csvfile:
    reader = csv.reader(csvfile, delimiter='|')
    for row in reader:
        print(row)
Exemple d’écriture#
import csv
with open('eggs.csv', 'w') as csvfile:
    writer = csv.writer(csvfile, delimiter='|')
    writer.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
    writer.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])

Indication

Les classes DictReader et DictWriter permettent de travailler sur des dictionnaires plutÎt que sur des séquences.

Créer un CLI#

L’utilisation du module argparse comme module « d’analyse d’arguments de ligne de commande » est recommandĂ© et est disponible dans la bibliothĂšque standard Python.

import argparse
parser = argparse.ArgumentParser(description="This is a usefull description")
parser.add_argument("echo", help="echo the string you use here")
parser.add_argument("square", help="display a square of a given number", type=int)
parser.add_argument("---verbosity", help="increase output verbosity") # optionnal args
args = parser.parse_args()
print(args.echo)
print(args.square)

Le module atexit permet d’enregistrer des appels de fonctions Ă  faire en sortie d’un programme pour faire le « mĂ©nage ». Les fonctions sont appelĂ©es :

  • en sortie « normale » de programme, y compris avec un sys.exit(1) par exemple.

  • si le programme se termine Ă  cause d’une exception.

  • si le programme se termine Ă  cause d’une erreur python (fonction appelĂ©e non existante, ou Ă©valuation de None + 1 par exemple).

  • si on fait un ctrl + C pour interrompre le programme (SIGINT).

  • mais pas d’appel si on tue le programme par SIGTERM (kill -15) ou SIGKILL (kill -9).

import atexit

def close_connection(connection):
    if connection:
        connection.close()

atexit.register(close_connection, connection)

Le module sqlite3#

Le module sqlite3, intégré dans la bibliothÚque standard de Python, a été écrit par Gerhard HÀring. Il fournit une interface SQL conforme à la spécification DB-API 2.0 décrite par la PEP 249.

Note

DB-API 2.0 est une API définie pour encourager la similarité entre les modules Python utilisés pour accéder aux bases de données.

Nous n’allons pas ici dĂ©tailler la spĂ©cification DB-API 2.0 mais dĂ©crire comment utilisĂ© succinctement le module sqlite3.

SQLite#

l s’agit d’une bibliothĂšque Ă©crite en langage C. Elle propose un moteur de base de donnĂ©es relationnelles accessible par le langage SQL, et implĂ©mente le standard SQL-92 et les propriĂ©tĂ©s ACID. Ce moteur est dĂ©veloppĂ© depuis 2000 D. Richard Hipp. La bibliothĂšque ainsi que son code source sont sous la licence du domaine public.

Particularité

Sa particularitĂ© est de ne pas reproduire le schĂ©ma habituel client-serveur mais d’ĂȘtre directement intĂ©grĂ©e aux programmes. L’accĂšs Ă  une base de donnĂ©es avec SQLite se fait par l’ouverture du fichier correspondant Ă  celle-ci. Chaque base de donnĂ©es est enregistrĂ©e dans un fichier qui lui est propre, avec ses dĂ©clarations, ses tables et ses index mais aussi ses donnĂ©es.

Il n’y a pas d’extension propre aux fichiers de base de donnĂ©es de SQLite, il est courant de rencontrer des extensions comme .sqlite ou .db, parfois suivie du numĂ©ro de version de la bibliothĂšque (.sqlite3, .db2, etc.).

Popularité

Il s’agit d’un des moteurs de bases de donnĂ©es les plus utilisĂ©s au monde, celui-ci est notamment utilisĂ© par Firefox, Skype, Google Gears, Android, iOS, des sites web, etc.

Une des raisons de son succĂšs est qu’elle est intĂ©grĂ©e dans les bibliothĂšques standard de nombreux langages comme Python, Ruby, PHP, C#, C++, etc. De plus son extrĂȘme lĂ©gĂšretĂ© (moins de 600 Kio), la rende populaire sur les systĂšmes embarquĂ©s.

Portabilité

Étant entiĂšrement Ă©crite en C-ANSI, la version normalisĂ©e du langage de programmation C, celle-ci est compilable sans modification sur toutes les architectures,

Ainsi les fichiers de base de donnĂ©es SQLite sont entiĂšrement indĂ©pendants du systĂšme d’exploitation et de l’architecture.

Type de données

Il existe plusieurs types d’affinitĂ© dans SQLite, dĂ©finissant la façon dont celui-ci va travailler lors de l’entrĂ©e des nouvelles donnĂ©es.

TEXT
NUMERIC
INTEGER
REAL
NONE

Note

Une interface en ligne de commande (en anglais command line interface, couramment abrĂ©gĂ©e CLI) est une interface homme-machine dans laquelle la communication entre l’utilisateur et l’ordinateur s’effectue en mode texte au sein d’un terminal (console).

SQLite embarque une interface en ligne de commande (CLI) simple et pratique pour crĂ©er et requĂȘter sur une base de donnĂ©es SQLite. Son utilisation se fait simplement par la commande sqlite3

$ sqlite3 -version
3.26.0 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt1
Quelques commandes utiles#
.exit       --Permet de quitter la commande CLI
.table      --Liste l'ensemble des tables de la base
.dbinfo     --Information sur la base
.dump       --Obtenir un dump sql de la base
.schema     --Obtenir le code de création de la base
.headers (on, off) --Affichage ou non des entĂȘtes
.mode (list, column, csv, ..) --Affichage en liste, etc.
.version    --Information de version
.import     --Import de données dans la base

Pour se conncter Ă  une base de donnĂ©es, il suffit d’indiquer le nom du fichier de base de donnĂ©es en argument de la commande sqlite3:

$ sqlite3 boardgames.sqlite3

Attention

Si le fichier existe alors la commande ouvre la base de données sinon celle-ci est créée avant ouverture.

Indication

Voici quelques astuces utiles avec la commande sqlite3 Ă  faire aprĂšs chaque lancement de la commande:

$ sqlite3 boardgames.sqlite3
sqlite> .headers on -- Display column name
sqlite> .mode columns -- Change display mode, try it
sqlite> .tables -- Show all db tables
sqlite> PRAGMA table_info(nom_table); -- Show structure of nom_table

Il est recommandĂ© d'Ă©crire les requĂȘtes (de crĂ©ation de tables par exemple) dans un fichier :code:`.sql` et de le jouer ensuite via la commande :code:`sqlite3`

.. code-block:: bash

$ sqlite3 boardgames.sqlite3
sqlite> .read creation_schema.sql

Rappel de SQL#

Création de tables#
CREATE TABLE contacts (
    id         INTEGER PRIMARY KEY,
    first_name TEXT    NOT NULL,
    last_name  TEXT    NOT NULL,
    email      TEXT,
    phone      TEXT    NOT NULL CHECK (length(phone) >= 10)
);

CREATE TABLE products (
    id           INTEGER         PRIMARY KEY,
    name         TEXT            NOT NULL,
    list_price   DECIMAL (10, 2) NOT NULL,
    discount     DECIMAL (10, 2) NOT NULL
    DEFAULT 0,
    CHECK (list_price >= discount AND
    discount >= 0 AND
    list_price >= 0)
);
Quelques requĂȘtes#
/* Sélection */
SELECT [ * / columns_name ] FROM tablename;

/* Compter */
SELECT COUNT(column_name) FROM table_name;

/* Trier */
SELECT column_name(s) FROM table_name ORDER BY column_name(s) ASC|DESC;

/* INSERTION */
INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, value2, value3,...);
/* MAJ */
UPDATE table_name SET column1=value, column2=value2,... WHERE some_column=some_value;
/* SUPPRESSION */
DELETE FROM table_name WHERE some_column=somevalue;
\end{minted}

Utilisation du module sqlite3#

Pour utiliser le module, vous devez d’abord crĂ©er une instance de la classe Connection qui reprĂ©sente la base de donnĂ©es.

Dans cet exemple, les données sont stockées dans le fichier example.sqlite3 :

import sqlite3
connection = sqlite3.connect('example.sqlite3')

Une fois que vous avez une instance de Connection, vous pouvez créer un objet Cursor et appeler sa méthode execute() pour exécuter des commandes SQL :

cursor = connection.cursor()

# Create table
cursor.execute('''CREATE TABLE stocks
(date text, trans text, symbol text, qty real, price real)''')

# Insert a row of data
cursor.execute("INSERT INTO stocks VALUES ('2006-01-05','BUY','RHAT',100,35.14)")

# Save (commit) the changes
connection.commit()

Attention

Par dĂ©faut, la connexion n’est pas en autocommit, il faut committer explicitement les ordres SQL via l’appel Ă  la mĂ©thode commit.

Avertissement

Vos requĂȘtes SQL utilisent souvent les valeurs de variables. Vous ne devez pas assembler votre requĂȘte Ă  l’aide d’opĂ©rations sur les chaĂźnes de caractĂšres, car cela n’est pas sĂ»r et rend votre programme vulnĂ©rable Ă  une attaque par injection SQL.

# Never do this -- insecure!
symbol = 'RHAT'
cursor.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

À la place, nous allons utiliser la capacitĂ© de substitution des paramĂštres. Placez un ? comme indicateur partout oĂč vous voulez utiliser une valeur, puis fournissez un tuple de valeurs comme second argument de la mĂ©thode execute().

values = ('RHAT',)
cursor.execute('SELECT * FROM stocks WHERE symbol=?', values)
# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
            ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
            ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
cursor.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

Pour récupérer des données aprÚs avoir exécuté une instruction SELECT, vous pouvez considérer le curseur comme un itérateur, appeler la méthode du curseur fetchone() pour récupérer une seule ligne correspondante ou appeler fetchall() pour obtenir une liste des lignes correspondantes.

cursor.execute('SELECT * FROM stocks WHERE symbol=?', ('RHAT',))
print(cursor.fetchone())
cursor.execute('SELECT * FROM stocks ORDER BY price')
print(cursor.fetchall())
for row in cusror.execute('SELECT * FROM stocks ORDER BY price'):
    print(row)

Pour en savoir plus direction la documentation sqlite3

Le module logging#

La journalisation (logging en anglais) est une façon de suivre les Ă©vĂ©nements qui ont lieu durant le fonctionnement d’un logiciel. Le dĂ©veloppeur du logiciel ajoute des appels Ă  l’outil de journalisation dans son code pour indiquer que certains Ă©vĂ©nements ont eu lieu.

Un Ă©vĂ©nement est dĂ©crit par un message descriptif, qui peut Ă©ventuellement contenir des donnĂ©es variables (c’est-Ă -dire qui peuvent ĂȘtre diffĂ©rentes pour chaque occurrence de l’évĂ©nement). Un Ă©vĂ©nement a aussi une importance que le dĂ©veloppeur lui attribue ; cette importance peut aussi ĂȘtre appelĂ©e niveau ou sĂ©vĂ©ritĂ©.

Quand utiliser logging ?

Le module logging fournit un ensemble de fonctions debug(), info(), warning(), error() et critical(). Pour plus d’information voir la documentation du module logging.

Pour déterminer quand employer quel niveau référez-vous au tableau suivant:

TĂąche que vous souhaitez mener

Le meilleur outil pour cette tĂąche

Sortie console d’un script

print()

Rapporter des Ă©vĂšnements qui ont lieu au cours du fonctionnement normal d’un programme

logging.info() ou logging.debug()

Émettre un avertissement (warning en anglais)

logging.warning()

Rapporter une erreur sans lever d’exception

logging.error(), logging.exception() ou logging.critical() selon le niveau de gravitĂ© de l’erreur

Les fonctions de journalisation sont nommĂ©es d’aprĂšs le niveau ou la sĂ©vĂ©ritĂ© des Ă©vĂšnements qu’elles suivent. Les niveaux standard et leurs applications sont dĂ©crits ci-dessous (par ordre croissant de sĂ©vĂ©ritĂ©) :

Niveau

Quand il est utilisé

DEBUG

Information dĂ©taillĂ©e, intĂ©ressante seulement lorsqu’on diagnostique un problĂšme.

INFO

Confirmation que tout fonctionne comme prévu.

WARNING

Quelque chose d’inattendu a eu lieu le logiciel fonctionne encore normalement.

ERROR

Un problĂšme plus sĂ©rieux, le logiciel n’a pas Ă©tĂ© capable de rĂ©aliser une tĂąche.

CRITICAL

Une erreur sĂ©rieuse, indiquant que le programme lui-mĂȘme pourrait ĂȘtre incapable de continuer Ă  fonctionner.

Note

Le niveau par défaut est WARNING, ce qui signifie que seuls les évÚnements de ce niveau et au-dessus sont suivis, sauf si le paquet logging est configuré pour faire autrement.

Les Ă©vĂšnements suivis peuvent ĂȘtre gĂ©rĂ©s de diffĂ©rentes façons. La maniĂšre la plus simple est de les afficher dans la console. Une autre mĂ©thode commune est de les Ă©crire dans un fichier.

import logging
logging.warning('Watch out!')  # will print a message to the console
logging.info('I told you so')  # will not print anything

Il est trĂšs commun d’enregistrer les Ă©vĂšnements dans un fichier:

import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

Attention

L’appel Ă  basicConfig() doit ĂȘtre fait avant un appel Ă  debug(), info(), etc. Comme l’objectif est d’avoir un outil de configuration simple et d’usage unique, seul le premier appel aura un effet, les appels suivants ne font rien.

Indication

Si votre programme est composĂ© de plusieurs modules, voici une façon d’organiser l’outil de journalisation :

myapp.py#
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log', level=logging.INFO)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()
mylib.py#
import logging

def do_something():
    logging.info('Doing something')

Pour changer le format utilisé pour afficher le message, vous devez préciser le format que vous souhaitez employer :

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
Exemple pour afficher l’heure avant le message#
logging.basicConfig(format='%(asctime)s %(message)s')