5.3 KiB
HackInScience
Julien Palard
CPython core dev
C'est historique.
Tu ne peux pas dire qu'HackInScience n'est pas né en 2014, parce que c'est historique.
Enseigner le Python
J'ai l'habitude.
Mais à des groupes de ~6 pendant ~3 jours.
C'est green
Enseigner le Python
Un jour on m'a proposé un groupe de 50 pendant 7 jours.
C'est pas green
Mais on est devs
Alors on a automatisé tout ce qui pouvait l'être.
::: notes
Pour passer du temps avec ceux qui en ont besoin.
HackInScience.org
C'est un petit Django, avec un peu de celery.
::: notes
Pour répartir les corrections sur des machines qui ne font que ça.
Démo en prod !
::: notes
Faire un tour côté teams, et un tour côté admin aussi !
C'est moche !
J'ai dit que j'étais dev, pas dev front, toute aide est la bienvenue.
::: notes c'est du bootstrap.
Je veux voir le code !
La première version fonctionnelle : 181 lignes de Python.
Ça devrait loger dans quelques slides ;)
::: notes
Aujourd'hui c'est 2700 lignes de code…
Django models
class Exercise(models.Model):
title = models.CharField(max_length=255)
check = models.TextField()
wording = models.TextField()
::: notes
Si vous connaissez déjà Django, concentrez-vous sur le contenu.
Django models
Un modèle Django c'est pas just pour l'ORM, ça aide à générer :
- des migrations de DB ;
- l'interface d'admin ;
- les formulaires ;
- les vues ;
- l'API ;
- l'autocomplétion dans l'IDE .
- les requêtes SQL (l'ORM).
::: notes
Un modèle permet au code de connaître de la structure de la base de donnée. Il n'est plus aveugle. C'est bien.
Django view
class ExerciseListView(LoginRequiredMixin, ListView):
model = Exercise
template_name = "hkis/exercises.html"
::: notes
Vous saviez que la MRO de Python garanti un héritage de gauche à droite ?
Ici il est garanti que les méthodes surchargées par LoginRequiredMixin
sont exécutées avant celles de ListView
.
Django view
class ExerciseView(LoginRequiredMixin, DetailView):
model = Exercise
template_name = "hkis/exercise.html"
Une petite API
class ExerciseSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Exercise
fields = '__all__'
class ExerciseViewSet(viewsets.ModelViewSet):
queryset = Exercise.objects.all()
serializer_class = ExerciseSerializer
router = routers.DefaultRouter()
router.register('exercises', ExerciseViewSet)
::: notes
Pourquoi un "Router" ? Parce que derrière cette ViewSet il y a plein de vues !
Démo time !
Une interface d'admin
from django.contrib import admin
from website.models import Answer, Exercise
admin.site.register(Answer)
admin.site.register(Exercise)
::: notes
Démo time !
C'est utilisé ?
Je n'ai pas de « pisteur », mais j'ai une DB.
En octobre 2022 : 730 personnes ont résolu 10_780 exercices
La moulinette a corrigé près de 50k rendus.
::: notes
Google Analytics c'est illégal. Mais je sais écrire du SQL.
Aucun tracker, aucune pub, aucun asset externe.
10k c'est peu ou beaucoup, je ne sais pas.
Ça tient la charge ?
Les exercices sont corrigés en environ 200ms,
Deux serveurs se répartissent le travail (et ils s'ennuient).
Les boucles infinies sont interrompues après 20s.
::: notes
Il est très facile d'ajouter un serveur de correction au besoin.
Côté sécu
Le code de chaque exercice est exécuté côté serveur, c'est un challenge niveau sécurité.
::: notes
Python est réputé pour ne pas être sandboxable, du moins pas depuis l'intérieur de l'interpréteur.
Namespaces Linux + seccomp + rlimit
Les exercices sont exécutés dans des environements très restreints.
seccomp
seccomp
(secure computing) c'est un outil du kernel Linux pour
limiter les appels systèmes que peuvent faire les programmes.
rlimit
rlimit
(resource limit) c'est un moyen de limiter les resources utilisées par un programme.
On limite par exemple à 20s de CPU, 2000 threads, 100 fichiers ouverts, 1GB de RAM, ….
namespaces
C'est aussi une fonctionalité du kernel Linux, elle permet de faire croire à un processus qu'il a une ressource « juste pour lui ».
C'est compliqué
Configurer setccomp, rlimit, les namespaces, la vie et le reste correctement, c'est pas simple.
Nous on utilise firejail.
C'est simple
Un petit appercu de comment on lance firejail :
FIREJAIL_OPTIONS = [
"--net=none",
"--shell=none",
"--x11=none",
"--private-dev",
"--private-tmp",
"--caps.drop=all",
"--nonewprivs",
"--nosound",
"--no3d",
"--noroot",
"--seccomp",
...
::: notes
C'est rassurant écrit comme ça, mais on sait que rien n'est parfait…
La rédaction d'exercices
Via l'interface d'admin
Via un repo git
L'hébergement d'instances locales
L'API
L'AFPy
La PyConFr
Gandi
Questions
- Mastodon : @mdk@mamot.fr
- XMPP : mdk@chapril.org
- HTTP : https://mdk.fr
- SMTP : julien@python.org
- Whatsapp : HAHAHA jamais.