Le module ctypes permet d'appeler des fonctions écrites en C dans du code Python.
Avant de pouvoir appeler une fonction, il faut définir le type des arguments qu'elle accepte ainsi que le type de la valeur qu'elle renvoie. Mais il arrive que la valeur renvoyée par la fonction nous envoie droit dans le mur avec une erreur de segmentation (segfault). Voyons comment éviter ça.
Partons d'un exemple concrêt avec le module MSS.
Sous GNU/Linux, pour récupérer les informations d'un écran, il faut passer par l'extension RandR du serveur X. En l'occurrence, la fonction XRRGetScreenResources()
. Le code complet est visible dans le fichier mss/linux.py, en plus concis ça donne :
from ctypes import cdll, POINTER
from ctypes.util import find_library
# Chargement de la bibliothèque RandR
xrandr = cdll.LoadLibrary(find_library('Xrandr'))
# Définition du type des arguments
xrandr.XRRGetScreenResources.argtypes = [POINTER(Display), POINTER(Display)]
# Définition du type de la valeur de retour
xrandr.XRRGetScreenResources.restype = POINTER(XRRScreenResources)
Le code ci-dessus fonctionnera parfaitement tant que l'extension RandR sera installée et activée. Sinon, segfault :
Xlib: extension "RANDR" missing on display "...".
Pour corriger ce problème, on peut vérifier le retour de la fonction en utilisant l'attribut errcheck. Il prend en paramètre un callable :
def check_null_pointer(result, func, arguments):
""" Valider la valeur retournée par une fonction ctypes pour prévenir les segfaults. """
if result == 0:
raise ValueError("On l'a échappée belle, un peu plus et c'était segfault dis donc !")
return arguments
# Hop, on passe la fonction de validation à XRRGetScreenResources
xrandr.XRRGetScreenResources.errcheck = check_null_pointer
check_null_pointer()
est notre garde-fou, une exception sera levée avant que le programme se fasse poutrer par une segfault.
Pour finir, il faudra appeler la fonction tel que :
try:
monitor = xrandr.XRRGetScreenResources(display, root)
except ValueError as ex:
print(ex)
Sources :