Sans répit, des hordes d’OP ont demandé sur Stackoverflow comment compiler un programme Python. La plupart du temps, pour des raisons d’obfuscation ou pour faire un joli .exe. Et inlassablement, les devs bienveillants leur répondaient que Python était un langage interprété, et donc non compilable.
Commença alors la lente fuite des impatients vers Go qui permettait de le faire si facilement.
Et puis arriva Cython, qui promettait de permettre de transformer du Python en C, afin de l’embarquer dans un autre code C, ou permettre des appels de C vers Python et inversement plus facile.
Cela visait les perfs, l’intégration, faire des dll/so en Python, et du coup, dans l’excitation, tout le monde a loupé un petit détail minuscule.
Les mecs avaient implémenté un compilateur Python => C complet.
En fait, Cython permet – et c’est la que c’est fun car c’est un effet de bord presque involontaire – de créer un programme compilé autonome en Python. Et avec la toolchain classique en plus.
Ça peut gérer les dépendances, et ça marche même avec des trucs compilés balèzes du genre PyQT.
Je sens le chapiteau se dresser sous la braguette, alors voici la démo…
Pour montrer qu’on ne fait pas que du “Hello World”, je vais utiliser des dépendances assez complexes : lxml (qui contient une extension C compilée), path.py, requests et docopt :
""" Download a file and save it to a location. Usage: downloader.py <target> [<location>] """ import requests import docopt from lxml.html import parse from path import path args = docopt.docopt(__doc__) p = path(args['<location>']).realpath() if p.isdir(): p = p / path(args['<target>']).namebase p.write_bytes(requests.get(args['<target>']).content) title = parse(p).getroot().find('head').find('title').text print('Downloaded "%s"' % title) |
Je ne m’encombre pas de gestion d’erreurs, juste suffisamment d’appels pour faire travailler les dépendances.
Qui dit compilation, dit environnement. Pour ma part, je suis sous Ubuntu 14.04 et je vais me compiler un petit programme en Python 3, avec lesquels il me faut installer Cython et pip pour Python 3, de quoi compiler du C, et les dépendances de notre script :
$ sudo apt-get install gcc cython3 python-pip3 python3-lxml $ pip3 install requests docopt path.py |
Je n’ai même pas eu besoin d’installer les headers de lxml. Vive le Dieu des geeks.
L’utilisation de Cython dans notre cas est assez simple : on lui dit de transformer notre module en module C. --embed
lui dit d’inclure l’interpréteur Python dedans.
$ cython3 downloader.py -o downloader.c --embed |
On obtient un fichier C bien copieux :
$ head downloader.c /* Generated by Cython 0.20.1post0 (Debian 0.20.1+git90-g0e6e38e-1ubuntu2) on Sun May 11 22:53:18 2014 */ #define PY_SSIZE_T_CLEAN #ifndef CYTHON_USE_PYLONG_INTERNALS #ifdef PYLONG_BITS_IN_DIGIT #define CYTHON_USE_PYLONG_INTERNALS 0 #else #include "pyconfig.h" #ifdef PYLONG_BITS_IN_DIGIT #define CYTHON_USE_PYLONG_INTERNALS 1 |
Il ne reste plus qu’à compiler ce dernier :
$ gcc -Os -I /usr/include/python3.4m downloader.c -o download -lpython3.4m -lpthread -lm -lutil -ldl |
J’ai mis -I /usr/include/python3.4m
et -lpython3.4m
car les headers de Python sont là dedans sur ma machine. Le reste, ce sont des options que j’ai copier / coller sur le Web car GCC a plus de flags qu’une ambassade américaine et que j’ai des choses plus importantes à retenir, comme par exemple la recette du guacamole.
On obtient un exécutable tout à fait exécutablatoire :
$ chmod u+x downloader $ ./downloader # docopt marche :) Usage: downloader.py <target> [<location>] |
Et ça télécharge comme prévu :
$ ./downloader http://sametmax.com index.html Downloaded "Sam & Max: Python, Django, Git et du cul | Deux développeurs en vadrouille qui se sortent les doigts du code" |
Le script Python original fait 472 octets, le binaire obtenu 28,7 ko. Mais ce n’est pas standalone, puisque je n’ai pas demandé la compilation des dépendances (je viens de le tester sur une autre Ubuntu, il pleure que requests n’est pas installé). Je vous laisse vous faire chier à trouver comment faire pour répondre à votre cas de figure exact, mais ça implique d’utiliser cython_freeze.
Après tout, cet article est intitulé “Peut on compiler un programme Python ?” et non “Tuto pour rendre un script Python stand alone”. Je ne suis pas fou.
Apparemment ça marche sous Windows et Mac, même si je n’ai pas de quoi tester sous la main (bon, si j’ai une partition Windows, mais faut rebooter, tout réinstaller, merde quoi…).
Donc si vous voulez faire une application en Python et la rendre stand alone, l’obfusquer, pondre un exe pour rendre le téléchargement facile, rassurer les gens ou simplement ignorer les mises à jours des libs de l’OS, Cython est une bonne piste.
Petit bonus, votre programme sera plus rapide, car il saute l’étape d’interprétation. Bien entendu, vous récoltez les galères qui viennent avec la compilation, à savoir les différentes architectures, le linking vers les libs qui peuvent changer de place ou de versions, etc.
Ca semble intéressant.
Est-il possible de lui passer directement un dossier contenant une hiérarchie de module ? Genre un projet Django ?
Aucune idée.
Et donc, la recette du guacamole ?
@Debnet: si il choppe tout seul les dépendances et que ton arborescence est bien importée dans ton programme, je suppose que ça devrait le faire.
Mais honnêtement si c’est pour les perf ça ne vaut pas le coup, car là où ça gagne vraiment c’est quand tu renseignes le type des variables et des arguments, pour utiliser les fonctions C directement.
Ça serait cool de faire un article sur l’optimisation via cython, tiens. Sauf que je sais pas si c’est très efficace en dehors des applis scientifiques…
Un article qui va trouver une bonne place dans les marques-pages. J’espère avoir bientôt l’occasion de tester. Merci !
@kontre: quelques avocats bien mous, une tomate très mûre, un oignon émincé au plus fin, du sel, du poivre, du curry + du cumin en poudre.
“Commença alors la lente fuite des impatients vers Go qui permettait de le faire si facilement.”
C’est exactement cela, en Go, un simple go build suffit.
En Python bienvenue dans la galère :
1 – Sur Windows, tu prends quelle version de Python 32 ou 64 ?
2 – Ensuite la bonne version de Cython 32 ou 64 ?
3- Pour finir tu prends quel MinGW le 32 ou 64 ?
Et bien si tu prends tout en 64, ça ne marchera pas pour des raisons obscures de la version 64 de Python…
Résultat tu remets tout en 32…bref t’en as pour 3 jours rien qu’à éplucher les listes pour voir quelles options pourries de gcc tu vas mettre. Donc tu finis avec un OS 64 et des applications 32 bits…
Alors franchement générer un exe pour une application Python, c’est la galère et je vous passe les py2exe, cfreeze et pyinstaller où là aussi prévois 4-5 jours pour comprendre le bin’s et chercher toutes les erreurs du net ;)
Alors oui bouge rapidos vers le Go ;)car distribuer une lib en Python c’est bien mais une appplication entière c’est la galère pas possible.
Je laisse ça là, ça pourrait servir :
http://freepythontips.wordpress.com/2014/03/03/using-py2exe-the-right-way/
@Roger: je risque pas de bouger rapido vers le GO :) Y a trop de lacunes par rapport à Python pour mes usages.
Comme j’ai vu que l’on causait distribution de paquets Python, j’ai écrit une petite note à ce sujet pour une distribution sous Windows que j’utilise régulièrement.
Cet article peut être un bon complément à votre article sur Cython. Mon article est orienté Kivy (kivy.org) qui réclame quelques astuces particulières pour la distribution.
http://hautefeuille.eu/distribute-python-windows-kivy.html
Ca fait un baille que je me dis que je dois me mettre à kivy (après un Pycon Fr où j’avais rencontré un des core devs). Arf, qui a le temps ? Qui a le temps ?
@Sam : en France il y de forte chance que tu aies rencontré Mathieu Virbel ou Gabriel Pettier. La communauté Kivy est sympathique et d’un assez haut niveau en compétence technique Python et C. Mathieu possède de solides compétences en OpenGL à mon sens.
@sam : tu peux essayer Kivy, tu verras, c’est assez intéressant et ça reprend des concepts de Qt avec le QML (on a les signaux de Qt par exemple à travers les properties Kivy). Les cores dev sont des personnes besogneuses, le code est de qualité et ils aiment nous faciliter la vie pour produire des exe standalone ou des Apk Android configurées aux petits oignons. J’aime bien la mentalité, la haute conpétence technique et la créativité du framework Kivy.
“compétence technique ” / “conpétence technique”
@kontre
Sinon pour le guacamole, je te suggère cette adresse :
http://www.sauce-piquante.fr
Elle te permettra de l’agrémenter à ta guise ;)
Vous dites que vous venez de ma part !
http://www.sauce-piquante.fr
En 44, cette adresse faisait Führer ;)
Merci julien.
Ma petite pierre, un bench bubble sort Python/Cython/Numba.
On voit bien ce que ça implique en terme de code si on veut vraiment avoir un gain.
Intéressant ton bench ! Il manque cependant un test avec numpy tout seul, et tu pourrais inclure la transformation de la liste en tableau numpy dans la fonction, pour une comparaison plus honnête. Et c’est bizarre la “lenteur” avec les memoryviews sur les petites listes.
Par contre tu dis :
Mais la solution la meilleure (pour les grosses listes) requiert très peu de modifs au final. Et ça donne toujours l’impression d’écrire du python.
Mon plus gros souci avec numba, c’est déjà que c’est la merde à compiler, et surtout que dès que j’ai voulu tester sur des fonctions “réelles” y’avait toujours des trucs tout con mais pas supportés. Alors que cython est vraiment bien compatible avec python.
J’y pense, est-ce que cython permet de faire des jeux vidéos 3D performants en python ? Pour le coup, ce serait vraiment cool !
Il y a déjà Blender pour ça.
Eve Online utilise python comme langage de script (comme World of Warcraft ou Wildstar utilisent Lua). Par contre le cœur n’est pas écrit en python, c’est pas fait pour ça.
Cython est performant parce qu’il permet de ne pas utiliser les types python (appeler une fonction en python est “lent”) mais directement les fonctions et variables en C. Donc faire tout un programme en python, sans utiliser les types python, c’est quand même ballot…
En quoi golang ne peut répondre à tes besoins ? Tu vises spécialement le langage ou les frameworks ? Le fait qu’il ne prends pas en charge le polymorphisme parametric, l’overloading d’opérateur, les generics ? Ou que le design du langage peut changer d’une version à une autre étant donné sa relative jeunesse ?
Car je suis sur un projet d’application web actuellement et je me tate à utiliser django qui est mature et vient avec un formidable langage, ou beego avec golang !
La question que je me pose : est-ce viable de travailler sur un gros projet avec un langage dynamique ? Je trouve que c’est à s’arracher les cheveux pour savoir si la fonction retourne une valeur ou une list etc …
Je pense que si youtube, dropox, pinterest, instragram, disqus et addons.mozilla.org sont codés en Python, c’est viable :)
Je n’utilise pas Go parce que :
– il y a moins de libs en go qu’en Python
– le langage est moins agréable à lire
– il est encore jeune, il manque tout un écosystème autour (python existe depuis 20 ans)
– il n’y a pas d’exceptions ni de sets en Go
– je préfère le duck typing au typage fort pour la plupart des projets
– python est déjà installé sur Linux et Mac sans rien à faire
– c’est pas pratique pour faire du scripting
– c’est pas pratique pour faire des interfaces graphiques
– beaucoup moins de doc pour go
– il n’y a aucun blog aussi bien que sametmax.com pour go
Il y a des avantages à Go : les goroutines et la compilation en un exe facile. Mais ça ne m’a jamais bloqué jusqu’ici, même si j’avoues qu’il était temps que quelque chose comme autobahn sorte pour Python, ça commençait à bien faire.
Mais la meilleure raison reste que j’aime Python, et il n’y a pas de motivations suffisante pour moi pour migrer. Une migration doit être motivée, sinon quel est le but ?
Je viens du monde .NET, et à l’époque de C# 3.5 il est été déjà facile de lancer une routine en asynchrone ! Mais depuis j’ai switché sur mac et je me suis mis à python/shell.
J’entends souvent parler des goroutines mais honnêtement je suppose qu’en python il doit bien exister un moyen similaire ! Rassure moi ?
Il me semblait que le duck typing était supporté par golang…
Ce que je comprends pas, c’est que plusieurs groupes essaient d’améliorer l’interpréteur, notamement pypy, pyrex et nouvellement pyston de dropbox mais aucuns n’est utilisable en production. (je me doute que c’est un gros travail ). ça commence à faire beaucoup je trouve.
Tout comme toi, je pense que python gagnerait à être plus connu si on pouvait construire un exécutable pour redistribuer un logiciel. Demain si je crée ma boite, je n’ai pas forcément envie de livrer mes sources !
Sauf si bien sur, je crée une application web …
Et toi tu n’es pas capable de nous pondre un builder ? :)
Il n’y a rien d’aussi pratique que les goroutines. On peut avoir les mêmes fonctionnalités, mais pas avec la même élégance en Python.
Pypy est utilisé en production.
On peut créer un exécutable avec pyinstaller, p2exe, cython, etc., c’est juste beaucoup moins facile qu’avec go.
Bonjour,
En 64bits, python34 et cx_freeze ou p2exe pas moyen de compiler, j’ai testé un petit prog, simple 10 lignes (python34 + Tkinter) il ne trouve pas encodings et une liste d’erreur sur finder.py. Une galère python et pourtant un très bon langage. D’après ce que j’ai lu, p2exe n’est pas encore dispo pour python V3 en 64 bits. Et revenir en 2.7 pour du 32 bits non merci. alors si infos… merci
Le packaging en python, c’est la merde.
Sous Linux ça va encore, python est installé de manière globale, un script suffit.
Sous windows c’est encore plus la mouise car il est obligatoire de créer un exécutable pour diffuser un programme. Ça devrait vraiment être natif…
Bonjour,
Oui, je constate, je n’arrive pas à compiler et pourtant les lib appropriées sont présentes. J’espère qu’un jour cela va changer car le langage python est bon. J’ai vu aussi AutoIt comme langage mais je ne suis pas attiré. Le C++ mais c’est un langage qui ne se devine pas, il faut une formation. Donc actuellement, j’attends mais cela me rage.
Je travaille sur Debian jessie et cette dernière a changé le nom de python-pip3 en python3-pip…
Trois jours de ma vie à éssayer de piger quel était le bon paquet… xD
Python est un langage dynamique, une fonction peut retourner une fonction, une classe … On peut écrire du code dans une string et l’exécuter avec exec(), et je ne vois pas comment on pourrait convertir en C un code dynamique puis le compiler
Merci beaucoup pour ce tutoriel , il va beaucoup me servir
J’adore ta prose. Merci :)
:)