Programmation système en Python
Modules
Un module est un fichier Python contenant des fonctions et des constantes.
import <module>
:sys.path
est parcouru pour trouver un fichier de nom<module>.py
, ce module est chargé, puis compilé en bytecode, puis exécuté. L’exécution du code peut être invisible (définition de classes, de fonctions), ou pas. Le code n’est exécuté qu’une seule fois, même si le module est importé plusieurs fois__name__
: nom du module courant s’il est importé, ou__main__
s’il est exécuté directement__file__
: chemin absolu vers le fichier/module courant. Utile dansos.path.join(os.path.dirname(__file__), 'file.local')
python -m <module_name>
: équivalent à l’exécution deimport <module_name>
(= chargement + exécution)python -m pip
: pippython -m venv
: virtualenvpython -m http.server
: serveur http pour fichiers statiques
import <module>
: les constantes et fonctions doivent être précédés de<module>.
from <module> import <func>, <const>
: les fonctions et constantes n’ont pas besoin d’être préfixées
# mymath.py PI=3.1415 def square(x): return x*x print('*** mymath loaded')
$ python > import mymath *** mymath loaded > mymath.PI 3.14159 > mymath.square(4) 16
$ python > from mymath import square > square(4) 16
Packages
Un package est un ensemble de modules organisé hiérarchiquement. C’est un répertoire, qui contient un fichier __init.py__
pour le distinguer d’un simple répertoire contenant des fichiers python.
from <package> import <module>
from <package>.<module> import <func>, <const>
Scripts
- shebang à mettre en en-tête des scripts :
#!/usr/bin/env python3
; la commandepython3
est alors cherchée dans le$PATH
de l’utilisateur - ajouter aussi :
# -*- coding: utf-8 -*-
pour éviter les problèmes d’encodage de caractères
Module os
Environnement
os.name
: nom du système d’exploitation, parmiposix
(Linux, MacOS, BSD), ‘nt’ (Windows), ‘java’ (Android)os.uname()
: renvoit un objet avec 5 attributs d’infos détaillées sur l’OSos.environ
: mapping de toutes les variables d’environnement (au moment de l’import)os.getenv(<varname>, <default>
: donne la valeur d’une variable d’environnement, avec une valeur par défault si elle n’existe pas
Système de fichiers
os.getcwd()
: renvoie le chemin d’accès courantos.walk(DIR)
: générateur qui parcours récursivement les fichiers et répertoires deDIR
; à chaque répertoire (y comprisDIR
) un triplet(dirpath, dirnames, filenames)
est généréos.remove(FILEPATH)
: supprime le fichier de chemin d’accèsFILEPATH
os.rmdir(DIRPATH)
: supprime le répertoire (qui doit être vide) de chemin d’accèsDIRPATH
os.path
os.path.join(PATH1, PATH2)
: renvoie un chemin d’accès, concaténation dePATH1
etPATH2
os.path.exists(PATH)
: renvoie True s’il existe un fichier ou un répertoire de chemin d’accèsPATH
os.path.getsize(FILEPATH)
: renvoie la taille en octet du fichierFILEPATH
os.path.samefile(PATH1, PATH2)
: True si les deux chemins d’accès font référence au même fichier (même i-node)
Module sys
sys.path
: liste des répertoires ou des .zip où python cherche les modules. Cette liste est augmenté des répertoires de la variable d’env.PYTHONPATH
si celle-ci est définiesys.modules
: dictionnaire de tous les modules chargéssys.argv
: liste des arguments lors d’une exécution en ligne de commandesys.version_info
: 5-uple qui décrit la version de l’interpréteur Python utilisé. Les deux premiers sont les numéros de version major et minor, par exemple 2 & 7, ou 3 & 6
Module urllib.parse
(package urllib
)
urllib.parse.urlsplit(url)
: décompose une url en ses constituants(addressing scheme, network location, path, query, fragment identifier)
urllib.parse.urlunsplit(parts)
: opération inverse deurlsplit
urllib.parse.urljoin(base, url)
: construit une url absolue en combinant une urlbase
avec une autre urlurl
. Siurl
est incomplète, ses éléments manquants sont pris dansbase
.
Exemple d’une commande Unix écrite en Python
Le script suivant supprime tous les fichiers .pyc à partir d’un répertoire donné et à tous les niveaux de profondeur.
cleanpyc.py#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os, sys def clean(directory_path): for root, _, filenames in os.walk(directory_path): for filename in filenames: if filename.endswith('.pyc'): os.remove(os.path.join(root, filename)) def main(): clean(sys.argv[1]) if __name__ == '__main__': main()
Il peut être utilisé :
- comme un script :
./cleanpyc.py <dir>
- comme une commande Unix: il faut créer un lien symbolique de
/usr/local/bin/cleanpyc
par exemple, vers le fichiercleanpyc.py
- comme un module exécutable :
python -m cleanpyc <dir>
- comme une fonction de librairie à importer dans un programme :
import ./cleanpyc
...
cleanpyc.clean(os.getenv('HOME'))
...
argparse
: extraction des arguments et des options
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import argparse def clean(directory_path, suffix): for root, _, filenames in os.walk(directory_path): for filename in filenames: if filename.endswith(suffix): os.remove(os.path.join(root, filename)) def main(): # build an empty parser parser = argparse.ArgumentParser() # define arguments parser.add_argument("dir", type=str, help="directory path") parser.add_argument("-s", "--suffix", type=str, default=".pyc", help="suffix of to be deleted files") # instruct parser to parse command line arguments args = parser.parse_args() clean(args.dir, args.suffix) if __name__ == '__main__': main()
Exercices à réaliser - consignes
- il y a 3 exercices :
strextract
,brklink
,ciqual
. Ils doivent être validés obligatoirement au travers de GitLab - il faut créer un seul projet Gitlab de titre “Python Scripting” et créer une issue (et donc une MR) par exercice
- le travail est à terminer obligatoirement à la fin de la période tampon
- le code doit être écrit selon les (bonnes) pratiques suivantes :
- les noms de variables doivent être choisis en anglais et permettre de deviner leur type et leur sens
- les noms de méthodes doivent être choisis en anglais et permettre de deviner leur sens et le type de la valeur renvoyée le cas échéant
- les exercices strextract et brklink peuvent et doivent être réalisés en moins de 50 et 100 lignes de code respectivement
Ressources à utiliser
strextract
Mots-clés, commandes : argparse, /usr/local/bin, re, walk
Ecrire un programme Python 3 qui parcourt récursivement tous les fichiers d’une hiérarchie (certains pourront être omis selon les options) et produit en sortie toutes les chaînes littérales (= les textes entourés par " ou ’) que contiennent ces fichiers.
Les chaînes seront affichées sous la forme où elles étaient dans le fichier, entourées des même symboles " ou ’.
Par exemple, si le contenu d’un des fichiers parcouru est:
<v-text-field
label="Nom d'utilisateur"
v-model='user.username'
:disabled="userId > 0" :error-messages="usernameErrors"
@input="$v.user.username.$touch(); $v.user.$touch()"
></v-text-field>
Ce fichier conduira à la production des lignes suivantes sur la sortie standard stdout
:
"Nom d'utilisateur"
'user.username'
"userId > 0"
"usernameErrors"
"$v.user.username.$touch(); $v.user.$touch()"
On ne cherchera pas à capturer les chaînes multi-lignes.
strextract [options] <dir>
- Le seul argument obligatoire de la commande est
dir
, chemin d’accès au répertoire à partir duquel est faite la recherche - Les options de la commande sont :
-h, --help
: affiche l’usage de la commande--suffix=<suffix>
: limite la recherche aux fichiers de suffixe<suffix>
--path
: chaque ligne produite est précédée du chemin d’accès au fichier associé suivi d’une tabulation-a, --all
: les fichiers cachés (ceux dont le nom commence par ‘.’) sont également inclus
- Le programme doit être installé comme une commande Unix accessible à tous les utilisateurs
$ strextract --path --suffix vue . ... /home/chris/myproject/User.vue "Nom d'utilisateur" /home/chris/myproject/User.vue 'user.username' /home/chris/myproject/User.vue "userId > 0" ...
brklnk
Mots-clés, commandes : requests, beautifulsoup, urljoin
Ecrire une commande brklnk en Python 3 qui parcourt tous les liens contenus dans une page web et affiche les liens cassés.
- Le programme doit être installé comme une commande Unix accessible à tous les utilisateurs
- La commande doit avoir les options suivantes :
--help
: affiche l’usage de la commande--depth=<n>
: profondeur de recherche<n>
(1 par défaut)
- Un lien qui n’a pas de nom de domaine, (ex: “domicile.html”) doit être accédé avec le protocole et le domaine de l’url courant
Tester sur :
Importation de données dans une base à partir d’un fichier csv
Mots-clés, commandes : psycopg2, csv
Écrire un programme Python3 qui importe dans une base de données Postgres ou Sqlite3 les données de nutrition de Ciqual exportées sous forme d’un fichier CSV.
On créera une base de données dont le schéma permet d’inclure toutes les informations contenues dans Ciqual. Plus précisément, la base de données devra comporter les 4 tables suivantes :
nutrient id: int (autoincrement) name string food id: string name: string grp_id: string (clé étrangère vers grp(id)) grp id: string name: string nutdata id: int (autoincrement) food_id: string (clé étrangère vers food(id)) nutrient_id: int (clé étrangère vers nutrient(id)) value: stringLes premiers éléments de cette base seront :
nutrient(1, "Eau (g/100g)") nutrient(2, "Protéines (g/100g)") ... grp("01", "entrées et plats composés") grp("02", "fruits, légumes, légumineuses et oléagineux") ... food("25600", "Céleri rémoulade, préemballé", "01") food("25601", "Salade de thon et légumes, appertisée, égouttée", "01") ... nutdata(1, "25600", 1, "78.5") nutdata(1, "25600", 2, "1.12") ...