Valider un wheel Python sur Mac cloud : tags et bibliothèques
Vous développez un projet Python sous Windows et confiez la compilation d’un paquet contenant des extensions natives à un Mac dans le cloud. Les journaux indiquent que le wheel a bien été généré. Pourtant, lorsqu’un collègue l’installe, il rencontre une incompatibilité d’architecture ou une bibliothèque dynamique introuvable. Le problème tient généralement moins à la création de l’archive qu’à son contenu, aux chemins de ses dépendances et à l’utilisation éventuelle, à votre insu, du répertoire des sources pendant les tests.
Nous partons ici d’un projet existant doté d’un fichier pyproject.toml pour mettre en place trois contrôles de validation : les tags, les bibliothèques natives et l’installation en environnement isolé. Il n’est pas question de publier le paquet dans un dépôt, ni de considérer qu’un test réussi sur une machine garantit la compatibilité avec tous les systèmes.
1. Fixer l’interpréteur et le périmètre de validation
Exécutez les commandes suivantes à la racine du projet sur le Mac distant. Utilisez un interpréteur Python déjà installé et conforme aux exigences du projet ; ne profitez pas de la validation pour le mettre à niveau.
python3 -m venv .wheel-build
.wheel-build/bin/python -m pip --version
.wheel-build/bin/python -c \
'import platform, sys; print(sys.version); print(platform.machine()); print(sys.executable)'
sw_vers
xcodebuild -version
Conservez la sortie avec l’identifiant du commit. La version de l’interpréteur, l’architecture du processus et la version du système font partie des paramètres de validation. Ce n’est pas parce que la machine est équipée d’une puce Apple Silicon que le processus Python en cours s’exécute nécessairement en arm64.
Définissez la version de Python à prendre en charge, la version minimale de macOS et la présence éventuelle d’extensions natives. Un paquet entièrement écrit en Python peut lui aussi dépendre de paquets natifs : vérifier uniquement le paquet principal ne suffit donc pas.
Le tag de plateforme figurant dans le nom d’un wheel est une déclaration de compatibilité, pas un convertisseur. Renommer un fichier arm64 en universal2 n’ajoute pas une seconde architecture au binaire.
2. Partir d’une compilation clairement identifiée
Vérifiez d’abord que dist ne contient aucun paquet issu d’une compilation précédente, puis générez le fichier à valider. Le backend de compilation et sa version doivent être fixés à l’aide du mécanisme de contraintes adopté par le projet. L’isolation de compilation activée par défaut ne verrouille pas automatiquement toutes les dépendances de compilation.
mkdir -p dist
find dist -maxdepth 1 -name '*.whl' -print
.wheel-build/bin/python -m pip wheel \
--no-deps --wheel-dir dist .
Si le premier contrôle affiche d’anciens fichiers, déplacez-les dans un répertoire d’archivage distinct avant de poursuivre. Ne mélangez pas les artefacts lors de la validation. --no-deps signifie seulement qu’aucun wheel ne sera généré pour les dépendances d’exécution ; cette option n’interdit pas le téléchargement de dépendances pendant la compilation.
Le contrôle suivant exige que le répertoire contienne exactement un wheel et affiche ses métadonnées internes :
.wheel-build/bin/python - <<'PY'
from pathlib import Path
from zipfile import ZipFile
wheels = list(Path("dist").glob("*.whl"))
if len(wheels) != 1:
raise SystemExit("Expected exactly one wheel")
wheel = wheels[0]
print(wheel.name)
with ZipFile(wheel) as archive:
metadata = [
name for name in archive.namelist()
if name.endswith(".dist-info/WHEEL")
]
if len(metadata) != 1:
raise SystemExit("Invalid wheel metadata")
print(archive.read(metadata[0]).decode("utf-8"))
PY
Vérifiez que Tag et les indications du nom de fichier correspondent à l’environnement de livraison visé. py3-none-any désigne généralement un paquet indépendant de la plateforme. Si le paquet contient malgré tout des exécutables natifs, il faut revoir son conditionnement plutôt que de se fier directement au tag.
3. Repérer les dépendances natives liées à la machine de compilation
Extrayez le wheel dans un répertoire temporaire afin d’inspecter les modules d’extension et les bibliothèques dynamiques. Le code suivant sert uniquement à l’inspection ; il n’exécute aucun programme contenu dans le paquet :
inspect_dir="$(mktemp -d)"
.wheel-build/bin/python - "$inspect_dir" <<'PY'
from pathlib import Path
from zipfile import ZipFile
import sys
wheel, = Path("dist").glob("*.whl")
with ZipFile(wheel) as archive:
archive.extractall(sys.argv[1])
PY
find "$inspect_dir" -type f \
\( -name '*.so' -o -name '*.dylib' \) \
-exec file {} \; -exec otool -L {} \;
Réservez cette procédure d’extraction aux artefacts de confiance produits par votre propre projet. N’extrayez pas directement dans le répertoire de travail une archive de provenance inconnue.
Recherchez en priorité les références aux répertoires personnels, aux espaces de travail de compilation et aux préfixes d’installation des gestionnaires de paquets locaux. Ces chemins absolus sont souvent absents des autres machines. Les chemins de bibliothèques système ne sont toutefois pas systématiquement erronés. De même, @rpath et @loader_path ne garantissent pas à eux seuls une configuration correcte : il faut aussi vérifier les bibliothèques réellement incluses dans le paquet et la configuration de leurs chemins de chargement.
Après toute modification, recompilez le wheel et recommencez la validation. Ne vous contentez pas de corriger les fichiers dans le répertoire d’extraction pour livrer ensuite le wheel d’origine, resté inchangé. Une fois l’inspection terminée, supprimez le répertoire temporaire créé pour ce contrôle.
4. Tester une véritable installation hors du répertoire des sources
Rassemblez d’abord le wheel et toutes ses dépendances d’exécution dans un répertoire distinct. Cette étape peut nécessiter un accès au réseau. Les versions des dépendances doivent être encadrées par les contraintes de publication du projet ; conservez également la liste finale des fichiers téléchargés.
mkdir -p wheelhouse
.wheel-build/bin/python -m pip download \
--only-binary=:all: --dest wheelhouse dist/*.whl
Si aucun paquet binaire compatible n’existe pour une dépendance, cette étape doit échouer. Ne basculez pas discrètement vers une compilation à partir des sources : cela changerait l’objet de la validation.
Les commandes ci-dessous créent un environnement hors de l’arborescence des sources. Lors de leur exécution, saisissez le véritable nom d’import du projet, qui peut différer du nom du paquet distribué :
read -r -p "Import module: " module
wheelhouse="$(pwd)/wheelhouse"
wheel="$(find "$(pwd)/dist" -maxdepth 1 -name '*.whl' -print)"
sandbox="$(mktemp -d)"
python3 -m venv "$sandbox/venv"
(
cd "$sandbox"
./venv/bin/python -m pip install \
--no-index --find-links "$wheelhouse" "$wheel"
./venv/bin/python -m pip check
./venv/bin/python -I - "$module" <<'PY'
import importlib
import sys
module = importlib.import_module(sys.argv[1])
print(getattr(module, "__file__", None))
print(list(getattr(module, "__path__", [])))
PY
)
Vérifiez que l’emplacement importé appartient au nouvel environnement, et non à l’arborescence des sources. pip check ne contrôle que les relations de dépendance déclarées. Il ne vérifie pas le chargement des bibliothèques natives et ne sollicite pas les fonctionnalités chargées à la demande. Exécutez donc aussi une opération métier qui fait réellement appel au module d’extension, par exemple le traitement d’un petit échantillon fixe avec une assertion sur le résultat attendu.
5. Consigner les critères de validation dans le dossier de publication
Pour chaque validation, conservez au minimum l’identifiant du commit, les informations sur l’interpréteur, le nom du wheel, son empreinte, la liste des dépendances et le résultat du test de bon fonctionnement métier. Calculez l’empreinte avec shasum -a 256 dist/*.whl pour vous assurer que le fichier livré est bien celui qui vient d’être testé.
En cas d’échec, intervenez au niveau concerné : si les tags ne correspondent pas, revoyez la cible de compilation ; s’il manque une bibliothèque dynamique, revoyez la collecte des dépendances ; si l’import ne fonctionne que depuis le répertoire des sources, vérifiez les règles de découverte des paquets et les déclarations de ressources. Ne masquez pas les fichiers manquants en élargissant les chemins de recherche.
Enfin, répétez l’installation et l’exécution sur la version minimale du système annoncée comme compatible, ainsi qu’avec les autres interpréteurs ciblés. Un test réussi sur un système plus récent prouve uniquement que cette combinaison fonctionne. Décrire clairement le périmètre vérifié est plus fiable que d’attribuer au paquet un tag de compatibilité plus large.
Questions fréquentes
Un build sur Apple Silicon produit-il forcément un wheel universal2 ?
Non. Les binaires natifs concernés doivent contenir arm64 et x86_64, avec des essais pour les deux architectures. Renommer le fichier ne suffit pas.
Pourquoi tester l'import hors du répertoire source ?
Les modules locaux peuvent masquer des fichiers absents du wheel. Installez le paquet dans un environnement virtuel neuf et vérifiez son import en mode isolé.
Confiez vos tâches à un Mac cloud dédié
Louez un Mac mini physique dédié et choisissez la région et la durée de facturation adaptées à votre activité. Après activation, consultez les informations de connexion dans la console. En cas de problème, ouvrez un ticket depuis la console.
Choisir une offre Mac cloud