HackInScience : automatiser l'enseignement de Python

Posté par  . Édité par Pierre Jarillon, Xavier Teyssier, Ysabeau, patrick_g et palm123. Modéré par Pierre Jarillon. Licence CC By‑SA.
37
15
jan.
2021
Éducation

HackInScience est un logiciel de publication et de correction automatique d'exercices, son instance hackinscience.org est une collection d'exercices Python.

HackInScience est né en 2014 : j'enseignais alors le Python en université (promos d'environ 70 élèves), et avec les collègues on s'est dit qu'il serait bon de passer plus de temps à aider les élèves qu'a les corriger, que la correction c'était automatisable, mais que passer du temps avec eux, ça ne l'était pas.

Deux "on jette tout et on recommence" plus tard, et deux confinements plus tard, on en est là :

Tout le code est open-source, c'est un simple Django pour présenter les exercices, des websockets pour recevoir sans attendre la correction du bot, et un Celery pour "load-balancer" les rendus aux serveurs de correction. Je n'enseigne plus en université mais le site étant toujours en ligne, et les confinements aidant, il a trouvé son public : 1500 exercices sont réussis par semaine en ce moment.

J’essaye de passer deux semaines par an à plein temps sur le projet, en 2019 j’ai travaillé l’internationalisation (être traducteur de la doc de Python et ne pas traduire ses propres projets n’était pas bien sérieux…), et en 2020 la notion de classement et d’équipes.

Du point de vue d'un élève

L'interface est minimaliste (d'abord parce que je suis plutôt nul en front :p) :

Un exercice hackinscience

À gauche l'énoncé, au milieu l'utilisateur rédige son code, et à droite la réponse du bot. C'est simple et il n'y a pas de place pour de la pub ;)

Du point de vue admin

C'est toute suite plus compliqué et mon but n'étant pas de rédiger la documentation ici je vais passer vite sur les détails les moins intéressants.

Pour l'installation d'une instance HackInScience je pense qu'un bon point de départ serait de s'inspirer de notre playbook Ansible, mais pour un test local un python manage.py runserver suffit.

La création d'exercices se passe dans un django-admin classique :

Admin: Création d'un exercice sur Hackinscience

Mais pour plus de confort j'ai pris l'habitude d'éditer les exercices localement (emacs étant un peu plus confortable que ACE Editor pour éditer du Python), de les versionner, et de tester mes modifications avant de les publier, pour ça j'utilise fetch.py et push.py dans le dossier scripts/.

La partie intéressante de la création d'un exercice, c'est son bot de correction : c'est aussi un script Python qui est appelé dans une sandbox à côté du rendu. La rédaction de ce script est totalement libre, la seule règle c'est : s'il print (sur stderr ou stdout) l'exercice est faux, sinon il est considéré juste.

Pour me faciliter le travail j'ai crée correction-helper qui lui même utilise friendly-traceback, à l'usage, rédiger un bot de correction ressemble à :

for message in test_messages:
    expected = bencode(message)
    with student_code():
        got = student_encode(message)
    if expected != got:
        message = _("Wrong answer while testing `{thing}`").format(thing=f"bencode({message!r})")
        if type(got) != bytes:
            message += "\n\n" + f"Expected a `bytes` instance, got a `{type(got)}`."
        fail(message, _("Got:"), code(got), _("Expected:"), code(expected))

Alors corriger du Python depuis du Python c'est assez simple, je peux soit utiliser subprocess.run pour exécuter le code de l'élève, soit importer ses fonctions et les tester, ça ressemble beaucoup à de la rédaction de tests unitaires, sauf qu'au lieu de se contenter d'un assert on rédige une description explicite de l'erreur, et parfois des conseils pour l'éviter.

Mais corriger du bash, du C, ou n'importe quoi d'autre doit être tout à fait possible aussi : rien n'empêche de rédiger une moulinette qui compile le code de l'élève puis le teste à coup de subprocess.run aussi.

Aller plus loin

  • # Sécurité ?

    Posté par  (site Web personnel) . Évalué à 7 (+5/-0).

    Et niveau sécurité/isolation comment sont gérés les programmes qui ne terminent pas, ceux qui consomment beaucoup de ressources, ceux qui essaient d'accéder à /etc/passwd, d'ouvrir des sockets, etc … ?

    J'ai souvent eu envie de faire des challenges avec ce genre d'approche mais j'ai jamais trop osé mettre ça entre des mains potentiellement hostiles (ou juste taquines).

    • [^] # Re: Sécurité ?

      Posté par  (site Web personnel) . Évalué à 4 (+2/-0).

      Le code semble utiliser firejail.

      Adhérer à l'April, ça vous tente ?

      • [^] # Re: Sécurité ?

        Posté par  . Évalué à 3 (+3/-0).

        Exact, j'utilise firejail (comme ça) pour exécuter le code des étudiants avec le moins de droits possible, même pas d'accès internet. J'imagine qu'avec du talent on doit pouvoir trouver des choses à faire, mais pour le moment (et j'en suis étonné) je n'ai vu personne tenter de contourner la sandbox.

  • # précisions sur le(s) bot(s)

    Posté par  . Évalué à 1 (+0/-0).

    Si j'ai bien compris la fin de la présentation, il faut un bot pour la correction automatique de l'exercice mai je n'ai pas bien compris s'il en faut un différent par exercice ou s'ils utilisent tous le même.

    D'après le gabarit de code posté, ce bot prend la saisie effectuée par l'élève et s'attend à ce que ça ne renvoie pas d'erreur en l'exécutant. Du coup, on peut mettre n'importe quel code Python pourvu que ça ne fasse pas d'erreur ?

    • [^] # Re: précisions sur le(s) bot(s)

      Posté par  . Évalué à 2 (+0/-0).

      Je pense que oui.

      Je me suis amusé sur un des premiers exercices à écrire plein de choses inutiles mais qui ne créaient pas d'erreur et c'est passé nickel crème.

      Surtout, ne pas tout prendre au sérieux !

    • [^] # Re: précisions sur le(s) bot(s)

      Posté par  . Évalué à 1 (+0/-0).

      Si j'ai bien compris la fin de la présentation, il faut un bot pour la correction automatique de l'exercice mai je n'ai pas bien compris s'il en faut un différent par exercice ou s'ils utilisent tous le même.

      J'écris un "correcteur" par exercice. Pour 72 exercices ça fait 5049 lignes de Python, soit en moyenne 70 lignes par "correcteur" (avec bien sur un peu de redondance : les imports toujours pareil, le setup de gettext, le main …). Je trouve ça plus propre et maintenable comme ça : quand je veux améliorer la correction de tel ou tel exercice, je sais rapidement où c'est (le script fetch.py me crée un dossier par exercice avec dedans : wording-fr.md, wording-en.md le texte présenté à droite, check.py le code de correction, …

      D'après le gabarit de code posté, ce bot prend la saisie effectuée par l'élève et s'attend à ce que ça ne renvoie pas d'erreur en l'exécutant. Du coup, on peut mettre n'importe quel code Python pourvu que ça ne fasse pas d'erreur ?

      Chaque "code de correction" prend l'approche qu'il veut, parfois j'exécute le code de l'élève avec subprocess.run(), parfois une seule fois, parfois plusieurs fois si je veux tester plusieurs paramètres. Sinon j'importe les fonctions de l'élève et je les exécute avec différents paramètres pour vérifier leur résultats. Dans le code d'exemple que j'ai mis, qui teste la bonne implémentation d'une fonction bencode par l'élève :

      • J'itére d'abord sur une collection de "messages à tester"
      • J'utilise ma fonction "de référence" bencode, pour encoder le message de test
      • J'exécute la fonction bencode de l'étudiant
      • Si nos deux résultats divergent, je lui forge une erreur. La fonction fail est celle qui print l'erreur, l'exercice sera donc considéré faux.

      Dans tous les cas si quelqu'un rend une fonction de trop, innutilisée, mais que son programme fonctionne, je ne pourrai pas m'en rendre compte. Par contre s'il se met à print en trop, ça risque de déplaire au bot.

      • [^] # Re: précisions sur le(s) bot(s)

        Posté par  . Évalué à 1 (+0/-0).

        Merci pour le retour. Ce n'est pas déconnant d'avoir un correcteur par exercice, c'est plus simple et plus propre (et ça répond à ma question).
        L'approche de correction est en fait celui des « tests » donc : vérification de sorties attendues pour des entrées données.

        • [^] # Re: précisions sur le(s) bot(s)

          Posté par  . Évalué à 3 (+2/-0).

          L'approche de correction est en fait celui des « tests » donc : vérification de sorties attendues pour des entrées données.

          Oui complètement, ça ressemble parfois comme deux gouttes d'eau à des tests unitaires. Sauf qu'au lieu de sortir un AssertionError j'essaye de rédiger un message qui explique pourquoi c'est faux, de manière à ce qu'un utilisateur ne soit jamais bloqué (utopique peut être).

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.