Python

Exécuter du code depuis Visual Studio Code

Avec un tel nom, il est évident que Visual Studio Code permet de lancer du code… mais comment faire pour exécuter mon fichier ?

Installer le plugin Code Runner

J’utilise depuis quelques temps le plugin Code Runner et il fait bien le taff. Il permet d’exécuter un snippet ou un fichier et la liste des langages supportés est longue comme le bras :

Run code snippet or code file for multiple languages: C, C++, Java, JavaScript, PHP, Python, Perl, Perl 6, Ruby, Go, Lua, Groovy, PowerShell, BAT/CMD, BASH/SH, F# Script, F# (.NET Core), C# Script, C# (.NET Core), VBScript, TypeScript, CoffeeScript, Scala, Swift, Julia, Crystal, OCaml Script, R, AppleScript, Elixir, Visual Basic .NET, Clojure, Haxe, Objective-C, Rust, Racket, Scheme, AutoHotkey, AutoIt, Kotlin, Dart, Free Pascal, Haskell, Nim, D, Lisp, Kit, V, and custom command

Rappel pour installer un plugin : dans la barre de gauche, il suffit cliquer sur le bouton des plugins dans la barre de gauche (c’est celui avec des carrés façon puzzle, hein) et de chercher le nom du plugin (ici code runner). Vous sélectionner votre plugin dans la liste et vous faites Install :

Exécuter du code

Voilà, c’est bon ! Vous pouvez exécuter du code ! La documentation nous dit qu’il y a plusieurs façons de faire :

To run code:

  • use shortcut Ctrl+Alt+N
  • or press F1 and then select/type Run Code,
  • or right click the Text Editor and then click Run Code in > editor context menu
  • or click Run Code button in editor title menu
  • or click Run Code button in context menu of file explorer

Exemple avec Python

Vous écrivez un fichier main.py, vous faites Crtl+Alt+N (ou une autre technique de votre choix) et c’est tout !

Vous vous demandez d’où vient cette commande python -u pour éxecuter mon fichier ? C’est tout simple : Code Runner a une liste de commandes pour les différents langages supportés et c’est comme ça qu’il réussit à exécuter mon fichier main.py.

A noter : il faut sélectionner votre fichier pour pouvoir l’exécuter (en cliquant dessus dans l’éditeur). Son extension est importante puisque Code Runner s’en sert pour déterminer le langage utilisé et donc comment l’exécuter.

Exemple avec C++

Essayons maintenant avec un petit main.cpp :

#include <iostream>

template <typename... Args>
void all_true(Args... args)
{
    auto ok = (args and ...);
    std::cout << (ok ? "OK" : "Nope") << '\n';
}

int main()
{
    all_true(true, 12 == 3 * 4, 6 * 111 / 66 == 11);
    all_true(42, 1 + 1 == 2);
}

Résultat :

[Running] cd "c:\Users\z19100018\Desktop\temp\article_visual_studio_code\"
          && g++ main.cpp -o main
          && "c:\Users\z19100018\Desktop\temp\article_visual_studio_code\"main
main.cpp: In function 'void all_true(Args ...)':
main.cpp:6:25: warning: fold-expressions only available with -std=c++1z or -std=gnu++1z
    auto ok = (args and ...);
                        ^~~
Nope
OK

[Done] exited with code=0 in 1.066 seconds

Ben oui : mon code utilise une fold expression de C++17, la commande par défaut pour le C++ ne précise pas le standard, et il est probable que mon MinGW64 n’utilise pas cette version du standard par défaut.

Accessoirement, je n’ai pas exactement envie de compiler sans les options -Wall -Wextra

Mais, alors ? Je fais quoi ? Et bien je modifie la commande par défaut !

Personnaliser les commandes de Code Runner

La commande par défaut peut ne pas vous convenir :

  • Pour Python, ça prend la version qui est dans le PATH mais je peux vouloir choisir d’utiliser la 3 plutôt que la 2.
  • Pour C++, je n’ai pas mes flags préférés.
  • Et ça sera peut-être / sans doute pareil pour d’autres langages…

La solution est d’utiliser de modifier le fichier settings.json de Visual Studio Code, le fichier magique qui contient les paramètres de Visual Studio Code. Appuyez sur F1 (la touche magique pour chercher partout), tapez « Settings » et choisissez « Preferences: Open Settings (JSON) » :

Ca ouvre votre settings.json et si vous n’avez pas encore rien configuré, il devrait juste contenir deux accolades : du JSON avec rien dedans.

Pour chaque langage que Code Runner supporte, vous pouvez ajouter une entrée dans code-runner.executorMap. Voici mon fichier avec ma commande pour C++ :

{
    "code-runner.executorMap": {
        "cpp": "cd $dir && g++ -std=c++17 -Wall -Wextra $fileName 
               -o $fileNameWithoutExt && $dir$fileNameWithoutExt"
    },
    "files.autoSave": "afterDelay"
}

Si vous vous demandez à quoi sert l’autre ligne, c’est simplement pour activer la sauvegarde automatique des fichiers. Oui, j’aime pas passer mon temps à faire Crtl+S.

Remarquez que la commande contient la variable $fileName : c’est pour ça qu’il faut sélectionner votre fichier pour pouvoir l’exécuter.

Si je refais Crtl+Alt+N sur mon fichier main.cpp, c’est évidemment bien mieux :

[Running] cd "c:\Users\z19100018\Desktop\temp\article_visual_studio_code\" 
         && g++ -std=c++17 -Wall -Wextra main.cpp -o main 
         && "c:\Users\z19100018\Desktop\temp\article_visual_studio_code\"main
Nope
OK

[Done] exited with code=0 in 1.291 seconds

Pour plus de détails

Je pense que l’essentiel a été dit. Pour plus de détails, je vous laisse parcourir la La documentation officielle.

Allez, faut que je vous laisse, j’ai du code à exécuter !


Boost pour appeler du C++ depuis Python

Il y a plusieurs années, je m’étais intéressé à la manière d’appeler du code C depuis Python. C’était tellement compliqué que je n’ai jamais eu envie d’essayer. J’ai récemment découvert l’existence de Boost Python, et là, j’ai eu envie d’essayer ! C’est parti !

Créer son module Python

La première étape est bien sûr d’écrire quelques fonctions ou classes C++ et de faire un peu de magie pour faire les wrappers pour Python. Pour créer un module Python, il suffit de créer une bibliothèque dynamique avec ce code et CMake est bien sûr l’outil de choix pour cela. Voici un exemple :


cmake_minimum_required(VERSION 3.10)
project(Boost_Python_Example)
# Find Boost
find_package(Boost COMPONENTS python3)
# Find Python
find_package(PythonInterp 3 REQUIRED)
find_package(PythonLibs 3 REQUIRED)
# Create library
add_library(mylibrary SHARED functions.cpp)
target_include_directories(mylibrary
PRIVATE ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS})
target_link_libraries(mylibrary ${Boost_LIBRARIES})
# Don't use the default 'lib' prefix
set_target_properties(mylibrary PROPERTIES PREFIX "")
message("install ${Python_SITELIB}")

view raw

CMakeLists.txt

hosted with ❤ by GitHub


#include <boost/python.hpp>
#include <iostream>
static int compute(int a, int b) {
return (a + b) * (a – b );
}
static void sayHello() {
std::cout << "Hello\n";
}
static void sayHelloTo(const std::string& person) {
std::cout << "Hello, " << person << '\n';
}
class Printer {
public:
Printer(const std::string& name) :
name_m(name) {
}
void print(const std::string& text) {
std::cout << '[' << name_m << ']' << text << '\n';
}
private:
std::string name_m;
};
BOOST_PYTHON_MODULE(mylibrary)
{
using namespace boost::python;
def("compute", compute);
def("say_hello", sayHello); // Python name != C++ name
def("sayHelloTo", sayHelloTo);
class_<Printer>("Printer", init<const std::string&>())
.def("print", &Printer::print);
}

view raw

functions.cpp

hosted with ❤ by GitHub

On builde la bibliothèque :

$ mkdir build
$ cd build/
$ cmake ..
-- The C compiler identification is GNU 7.3.0
-- The CXX compiler identification is GNU 7.3.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMake Warning at /usr/share/cmake-3.10/Modules/FindBoost.cmake:1626 (message):
  No header defined for python3; skipping header check
Call Stack (most recent call first):
  CMakeLists.txt:5 (find_package)


-- Boost version: 1.65.1
-- Found the following Boost libraries:
--   python3
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.6.7", minimum required is "3") 
-- Found PythonLibs: /usr/lib/i386-linux-gnu/libpython3.6m.so (found suitable version "3.6.7", minimum required is "3") 
-- Configuring done
-- Generating done
-- Build files have been written to: /home/pierre/Documents/boost_python/build

$ cmake --build .
Scanning dependencies of target mylibrary
[ 50%] Building CXX object CMakeFiles/mylibrary.dir/functions.cpp.o
[100%] Linking CXX shared library mylibrary.so
[100%] Built target mylibrary

On peut maintenant exécuter Python et tester notre module :

$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import mylibrary
>>> mylibrary.say_hello()
Hello
>>> p = mylibrary.Printer("B&W printer")
>>> p.print("bla bla")
[B&W printer]bla bla
>>>help(mylibrary)
Help on module mylibrary:

NAME
    mylibrary

CLASSES
    Boost.Python.instance(builtins.object)
        Printer
    
    class Printer(Boost.Python.instance)
     |  Method resolution order:
     |      Printer
     |      Boost.Python.instance
     |      builtins.object
     |  
     |  Methods defined here:
     |  
     |  __init__(...)
     |      __init__( (object)arg1, (str)arg2) -> None :
     |      
     |          C++ signature :
     |              void __init__(_object*,std::__cxx11::basic_string<char, std::char_traits, std::allocator >)
     |  
     |  __reduce__ = (...)
     |  
     |  print(...)
     |      print( (Printer)arg1, (str)arg2) -> None :
     |      
     |          C++ signature :
     |              void print(Printer {lvalue},std::__cxx11::basic_string<char, std::char_traits, std::allocator >)
     |  
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |  
     |  __instance_size__ = 32
     |  
     |  ----------------------------------------------------------------------
     |  Methods inherited from Boost.Python.instance:
     |  
     |  __new__(*args, **kwargs) from Boost.Python.class
     |      Create and return a new object.  See help(type) for accurate signature.
     |  
     |  ----------------------------------------------------------------------
     |  Data descriptors inherited from Boost.Python.instance:
     |  
     |  __dict__
     |  
     |  __weakref__

FUNCTIONS
    compute(...)
        compute( (int)arg1, (int)arg2) -> int :
        
            C++ signature :
                int compute(int,int)
    
    sayHelloTo(...)
        sayHelloTo( (str)arg1) -> None :
        
            C++ signature :
                void sayHelloTo(std::__cxx11::basic_string<char, std::char_traits, std::allocator >)
    
    say_hello(...)
        say_hello() -> None :
        
            C++ signature :
                void say_hello()

FILE
    /home/pierre/Documents/boost_python/build/mylibrary.so

Notez que import mylibrary fonction.ne parce que mylibrary.so est dans le dossier d’où Python est lancé. Il y a moyen de l’installer dans le dossier dédié de Python, comme expliqué ici, pour y avoir accès depuis n’importe où.

Pour aller plus loin

Il existe le GitHub parfait pour aller plus loin : boost::python examples.

Ah ! Si ça marchait toujours du premier coup…

Évidemment, tout n’a pas marché du premier coup… Je me suis tapé quelques erreurs sympas avant d’arriver à quelque chose de fonctionnel.

Il faut bien sûr que Boost et Python soient installés :

sudo apt install libboost-all-dev python3-dev

J’ai eu une erreur magnifique de compilation à cause d’un header Python :

$ make
[ 50%] Building CXX object CMakeFiles/mylibrary.dir/functions.cpp.o
In file included from /usr/include/boost/python/detail/prefix.hpp:13:0,
                 from /usr/include/boost/python/args.hpp:8,
                 from /usr/include/boost/python.hpp:11,
                 from /home/pierre/Documents/boost_python/functions.cpp:1:
/usr/include/boost/python/detail/wrap_python.hpp:50:11: fatal error: pyconfig.h: No such file or directory
 # include 
           ^~~~~~~~~~~~
compilation terminated.

Ce fichier est apporté par le paquet python3-dev et il était bien présent sur mon PC :

$ locate pyconfig.h
/usr/include/i386-linux-gnu/python2.7/pyconfig.h
/usr/include/i386-linux-gnu/python3.6m/pyconfig.h
/usr/include/python2.7/pyconfig.h
/usr/include/python3.6m/pyconfig.h

Le problème était donc d’ajouter le dossier /usr/include/python3.6m/ à l’include path. Une solution est de l’ajouter au path avant de compiler. Il y a beaucoup mieux en s’appuyant que les capacités de CMake à trouver Python : si Python est trouvé, la variable PYTHON_INCLUDE_DIRS est renseignée et il suffit de l’ajouter en tant qu’include directory comme fait dans mon CMakeLists.txt ci-dessus.

La première fois que j’ai tenté d’importer mon module, j’ai eu une erreur de version de Python :

$ python3
Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import mylibrary
Traceback (most recent call last):
  File "", line 1, in 
ImportError: /usr/lib/i386-linux-gnu/libboost_python-py27.so.1.65.1: undefined symbol: PyClass_Type
>>>
[3]+  Stopped                 python3

En effet, find_package(Boost COMPONENTS python) trouvait la variante pour Python 2. Il m’a fallu rajouter un 3, ce qui donne find_package(Boost COMPONENTS python3). La documentation de FindBoost a un paragraphe à ce sujet :

Note that Boost Python components require a Python version suffix (Boost 1.67 and later), e.g. python36 or python27 for the versions built against Python 3.6 and 2.7, respectively. This also applies to additional components using Python including mpi_python and numpy. Earlier Boost releases may use distribution-specific suffixes such as 2, 3 or 2.7. These may also be used as suffixes, but note that they are not portable.


Installation d’Autobahn|Testsuite sous Xubuntu

L’année dernière, dans le cadre du travail, j’ai fait une implémentation de la partie client du protocole WebSocket, décrit par la RFC 6455, pour une utilisation dans un environnement Java embarqué contraint. Au début, j’utilisais un petit serveur, écrit en Java SE,  pour avoir quelques tests simples. Quand mon implémentation à commencer à devenir mature, j’ai cherché comment tester de manière un peu plus complète ma bibliothèque et j’ai trouvé Autobahn|Testsuite. C’est une test suite automatique, écrite en Python 2 et elle est plutôt réputée pour ce protocole, à en juger par la liste affirmée des utilisateurs. Elle est en tout cas, elle est suffisamment réputée pour qu’Oracle fasse un article quand leur implémentation de référence de WebSocket en Java a atteint pour la première fois un score de 100 %. J’ai remis en branle cette test suite cette semaine car je vais me resservir de cette bibliothèque pour un nouveau projet.

Le site officiel donne une procédure d’installation qui est en apparence simple mais j’ai rencontré plusieurs problèmes en l’installant sous Xubutu. J’écris cet article pour retracer les problèmes et les solutions.

La première étape est d’installer les dépendance décrites. Python 2.7 est installé par défaut, il me suffit donc d’installer les modules pip et Twisted :

sudo apt-get install python-pip
sudo apt-get install python-twisted

On peut ensuite passer à l’installation de la test suite en elle-même. Je vous conseille de demander les droits root pour éviter des erreurs telles que error: could not create '/usr/local/lib/python2.7/dist-packages/autobahntestsuite': Permission denied. Dans le terminal :

sudo pip install autobahntestsuite>

Après des téléchargements et un peu de défilement dans la console, vous rencontrerez sans doute une erreur comme celle-ci :

wsaccel/utf8validator.c:8:22: fatal error: pyconfig.h: No such file or directory

 #include "pyconfig.h"

                      ^

compilation terminated.

error: command 'i686-linux-gnu-gcc' failed with exit status 1

Pour que l’installation réussisse, j’ai dû installé quelques paquets supplémentaires :

sudo apt-get install python-dev
sudo apt-get install libffi-dev
sudo apt-get install libssl-dev

L’installation est maintenant terminée ! Il n’y a plus qu’à lancer le test d’installation, qui vérifie que tout s’est bien installé :

$ wstest --help
Traceback (most recent call last):
  File "/usr/local/bin/wstest", line 5, in 
    from pkg_resources import load_entry_point
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 2749, in 
    working_set = WorkingSet._build_master()
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 444, in _build_master
    ws.require(__requires__)
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 725, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 628, in resolve
    raise DistributionNotFound(req)
pkg_resources.DistributionNotFound: characteristic>=14.0.0
$ echo $?
1

Caramba ! Encore raté ! Python ne possède pas le module characteristic ou alors il en possède une version trop ancienne. pip est la solution pour installer des modules Python :

sudo pip install characteristic

Histoire de vous faire gagner du temps, je vous annonce qu’il manque aussi d’autres modules, installez-les avant de retenter wtest :

sudo pip install pyasn1-modules
sudo pip install cryptography
sudo pip install unittest2

Finalement :

$ wstest --help && echo $?
Usage: wstest [options]
Options:
  -d, --debug            Debug output [default: off].
  -a, --autobahnversion  Print version information for Autobahn and
                         AutobahnTestSuite.
  -m, --mode=            Test mode, one of: echoserver, echoclient,
                         broadcastclient, broadcastserver, fuzzingserver,
                         fuzzingclient, testeeserver, testeeclient, massconnect,
                         serializer [required]
  -t, --testset=         Run a test set from an import test spec.
  -s, --spec=            Test specification file [required in some modes].
  -o, --outfile=         Output filename for modes that generate testdata.
  -w, --wsuri=           WebSocket URI [required in some modes].
  -u, --webport=         Web port for running an embedded HTTP Web server;
                         defaults to 8080; set to 0 to disable. [optionally used
                         in some modes: fuzzingserver, echoserver,
                         broadcastserver, wsperfmaster]. [default: 8080]
  -i, --ident=           Testee client identifier [optional for client testees].
  -k, --key=             Server private key file for secure WebSocket (WSS)
                         [required in server modes for WSS].
  -c, --cert=            Server certificate file for secure WebSocket (WSS)
                         [required in server modes for WSS].
      --version          Display Twisted version and exit.
      --help             Display this help and exit.

0

\o/

Quand j’avais réussi à faire fonctionner wtest au travail, l’aventure n’était pas vraiment terminée. J’avais fait cela sur mon PC avec Internet mais je souhaitais monter un serveur de tests sur le réseau privé non relié à Internet… Il y avait une solution !

pip install --download . autobahntestsuite

Cette commande permet de télécharger toutes les dépendances nécessaires (y compris les récalcitrants pyasn1-modules et autre characteristic…) dans le dossier courant. On peut ensuite déplacer ce dossier sur le PC non connecté à Internet et lancer une installation hors-ligne. En effet, pip est capable d’installer à partir de dépendances qu’il cherche dans le dossier précisé plutôt que d’aller les chercher dans ses dépôts habituels :

pip install --no-index --find-links="/path/to/downloaded/dependencies" packagename

Dans mon cas, les dépendances n’étaient pas super bien gérées. J’ai eu des erreurs tels que distutils.errors.DistutilsError: Could not find suitable distribution for Requirement.parse('enum34') que j’ai corrigé en installant manuellement enum34 et cffi. Ces dépendances avaient pourtant bien été téléchargées mais l’installation du module autobahntestsuite n’arrivait pas à les installer toute seule.

Maintenant que tout est installé, on va quand même essayé de faire un vrai test du protocole, non ? Vous trouverez des détails sur l’utilisation de wtest sur cette page dédiée. Pour faire simple, je vais lancer un serveur de tests et je vais y connecter mon navigateur pour quel score il obtient. Le serveur se lance avec la commande suivante :

$ wstest -m fuzzingserver
Auto-generating spec file 'fuzzingserver.json'
Loading spec from /home/pgradot/fuzzingserver.json

Using Twisted reactor class 
Using UTF8 Validator class 
Using XOR Masker classes 

Autobahn WebSockets 0.7.2/0.10.9 Fuzzing Server (Port 9001)
Ok, will run 521 test cases for any clients connecting
Cases = ['1.1.1', '1.1.2', '1.1.3', '1.1.4', '1.1.
...
'13.7.16', '13.7.17', '13.7.18']

La commande ne rend pas la main tant que le serveur tourne. J’ai tronqué (avec ...) la liste des 521 test cases qui sont en attente de clients. Il suffit ensuite de lancer Firefox et d’aller à l’adresse http://localhost:8080 :

autobahn start test

Vous l’aurez deviner, il suffit de cliquer sur Test your browser pour lancer le test :

autobahn test running

On voit le compteur de tests exécutés se mettre à jour sur la page. Dans le terminal, on peut voir les traces d’exécution du serveur. En voici un extrait :

Running test case ID 12.5.11 for agent Firefox/40.0-20100101 from peer tcp4:127.0.0.1:60352
Running test case ID 12.5.12 for agent Firefox/40.0-20100101 from peer tcp4:127.0.0.1:60353
Running test case ID 12.5.13 for agent Firefox/40.0-20100101 from peer tcp4:127.0.0.1:60354

Quand les tests sont terminés, il faut cliquer sur le bouton Update Reports (Manual) pour mettre à jour les résultats, retourner à la page précédente et afficher les résultats des tests locaux :

autobahn results

Cette version 40 de Firefox ne fait pas un score de 100 %, certains tests sur les trames de fermeture de connexion sont à « fail« . Il affiche tout de même de très bons résultats 🙂

Bons tests !


Python is beautiful (again)

J’aime bien Python. Mon utilisation de ce langage est fortement axé sur l’écriture de petits scripts pour décortiquer les traces en console d’autres d’applications. Et pour ça, Python est très fort. Déjà parce qu’on peut commencer dans l’interpréteur interactif et ensuite mettre le code dans un fichier *.py pour le conserver. Ensuite, parce qu’il y a des écritures très efficaces pour faire cela. Dans cet article, je vous montre mon dernier script pour dire une nouvelle fois que « Python is beautiful ».

Pour commencer, voici la trace d’exécution. Il a appairage de deux périphériques au début, puis deux threads font des ON et des OFF, tandis qu’un troisième thread traite les communications et affiche les temps de traitement :

0
SwitchDeviceListener.deviceRegistered()
OFF
SwitchDeviceListener.deviceRegistered()
OFF
110
78
ON
390
ON
78
OFF
437
OFF
79
ON
421
ON
94
OFF
421
OFF
95
ON
405
[...]

L’objectif ici est de trouver les temps minimum, maximum et moyen. Pour cela, les listes marchent à merveille :

  • On crée une liste avec toutes les lignes du fichier.
  • On en dérive une liste ne contenant que les éléments qui ne sont pas dans une liste d’éléments à exclure grâce à not in.
  • On transforme les éléments de type string en int.
  • On utilise les opérateurs adaptés pour récupérer les valeurs recherchées.
from statistics import mean, median

# Read file content
f = open("C:\\Users\\pgradot\\Desktop\\data.txt", "r")
lines = f.readlines()
f.close()

# Clean content
to_exclude = ["SwitchDeviceListener.deviceRegistered()\n", "ON\n",  "OFF\n"]
cleaned = [line for line in lines if line not in to_exclude]
print(cleaned)

# Convert to numbers
numbers = list(map(int, cleaned))
print(numbers)

# Print statistics
print("Min = ", min(numbers))
print("Max = ", max(numbers))
print("Mean = ", mean(numbers))
print("Median = ", median(numbers))

Le script nous donne les valeurs recherchées :

Min =  0
Max =  578
Mean =  272.37404580152673
Median =  235.0

Le module statistics a été ajouté en version 3.4 de Python.

C’est beau, on est contents 🙂


Appeler Python depuis Ant

Aujourd’hui, pour des raisons professionnelles, j’ai eu besoin d’appeler Python depuis Ant. Il était en effet bien plus facile d’effectuer le traitement souhaité en Python qu’en Ant. Cet article retrace les points principaux pour appeler un script Python depuis un script Ant.

Pour l’exemple, je crée dans Eclipse un projet de type général nommé PythonFromAnt qui contient le script Ant, ant.xml, et le script Python, script.py.

Le script Python est très simple, il affiche son nom et renvoie le code passé via la ligne de commande :

import sys
print("hello, this script is " + sys.argv[0])
ret = int(sys.argv[1])
print("exit code = {0}".format(ret))
sys.exit(int(sys.argv[1]))

Pour appeler Python depuis Ant, il suffit d’utiliser la tâche Ant exec :

<?xml version="1.0" encoding="UTF-8"?>
<project name="example" default="main">

  <target name="main">
    <!– La propriété ant.file.PROJECT_NAME (ici, PROJECT_NAME="example") est toujours fournie par Ant –>
    <dirname file="{ant.file.example}" property="current.folder"/>

    <!– Le script Python retournera 0, signifiant que tout s’est bien passé –>
    <exec executable="python" dir="${current.folder}">
      <arg line="script.py 0"/>
    </exec>

    <!– Le script Python retournera 1 mais l’exécution Ant continue. On voit le résultat en rouge dans la console –>
    <exec executable="python" dir="${current.folder}">
      <arg line="script.py 1"/>
    </exec>

    <!– Le script Python retournera 1 et cela activera le "failonerror" d’Ant –>
    <exec executable="python" dir="${current.folder}" failonerror="true">
      <arg line="script.py 1"/>
    </exec>

    <echo>Ce message ne sera pas affiché</echo>
  </target>

</project>

Pour faire simple, j’ai ajouté Python au path, pour ne pas avoir à donner le chemin complet à exec. En arguments, je donne le chemin vers mon script Python et les paramètres à fournir à ce script. L’attribut dir permet de s’assurer que Python est lancé dans le dossier où se trouve le script qu’il cherche. Mon exemple est très simple, on aurait sans doute pu faire sans, mais dans un environnement de build plus important, le script Ant peut-être exécuté depuis un autre dossier et Python renverra une erreur car il ne trouve pas script.py.

Exécutez le script en faisant un clic-droit Run as…Ant build pour avoir la sortie suivante dans la console d’Eclipse :

Python from Ant - Sortie console


Concevoir un site comme celui-ci avec WordPress.com
Commencer