{
  "chapter": {
    "id": "debogage-terminale",
    "level": "terminale",
    "theme": "Programmation",
    "title": "Mise au point et débogage",
    "description": "Mise au point d'un programme : tests d'intégration,\nprofilage (en temps et en mémoire), gestion des\nexceptions avec `try` et `except`, analyse des\njournaux d'exécution, débogueur interactif et\nméthodes systématiques de réduction au cas\nminimal et d'explication à voix haute.",
    "prerequisites": [],
    "references": []
  },
  "questions": [
    {
      "id": "q01",
      "difficulty": 1,
      "skills": [
        "exception"
      ],
      "title": "Notion d'exception",
      "statement": "Qu'est-ce qu'une **exception** en Python ?",
      "options": [
        {
          "text": "Un type de variable particulier",
          "correct": false,
          "feedback": "Une exception est un événement de\nl'exécution, pas un type de donnée que\nl'on déclare comme on déclarerait un\nentier ou une chaîne.\n"
        },
        {
          "text": "Un simple avertissement qui n'interrompt pas du tout le programme",
          "correct": false,
          "feedback": "Cette description correspond à un\n*warning*. Une exception non capturée,\nau contraire, interrompt l'exécution du\nprogramme.\n"
        },
        {
          "text": "Une erreur signalée à l'exécution, qui interrompt le programme",
          "correct": true,
          "feedback": "Quelques exemples classiques :\n`ValueError`, `TypeError`, `IndexError`,\n`KeyError`. C'est le mécanisme standard\npour signaler une condition inattendue\npendant l'exécution.\n"
        },
        {
          "text": "Un commentaire spécial inséré dans le code",
          "correct": false,
          "feedback": "Une exception n'a aucun rapport avec un\ncommentaire. Les commentaires en Python\ncommencent par le caractère `#` et n'ont\naucun effet sur l'exécution.\n"
        }
      ],
      "explanation": "Les exceptions remontent la pile des appels\nde fonctions jusqu'à un bloc `try / except`\nqui les capture. En l'absence d'un tel\nbloc, le programme s'arrête en affichant\nla trace de l'erreur."
    },
    {
      "id": "q02",
      "difficulty": 1,
      "skills": [
        "try-except"
      ],
      "title": "Le bloc try / except",
      "statement": "Comment **gérer** une exception sans que le\nprogramme ne s'interrompe ?",
      "options": [
        {
          "text": "Aucun moyen ne permet d'éviter l'arrêt du programme",
          "correct": false,
          "feedback": "Python fournit précisément le bloc\n`try / except` pour intercepter une\nexception et poursuivre l'exécution.\n"
        },
        {
          "text": "```python\ntry:\n    risque()\nexcept Exception as e:\n    print(\"Erreur :\", e)\n```\n",
          "correct": true,
          "feedback": "Le bloc `try` tente l'exécution. En\ncas d'exception, le bloc `except`\nprend le relais et permet de gérer\nproprement l'erreur.\n"
        },
        {
          "text": "```python\ntry {\n    risque()\n} catch (Exception e) {\n    print(\"Erreur :\", e)\n}\n```\n",
          "correct": false,
          "feedback": "Cette syntaxe à accolades, avec\n`catch`, est celle de Java ou de C++,\npas celle de Python, qui emploie `try`\net `except` suivis de deux-points et\nd'un bloc indenté.\n"
        },
        {
          "text": "```python\nresultat = risque()\nif resultat == error:\n    print(\"Erreur :\", resultat)\n```\n",
          "correct": false,
          "feedback": "Ce code ne gère rien : si `risque()`\nlève une exception, le programme\ns'arrête avant même d'atteindre le\n`if`. La gestion des erreurs ne\nrepose pas sur une comparaison à une\nvaleur spéciale, mais sur le mécanisme\ndes exceptions.\n"
        }
      ],
      "explanation": "Variantes utiles :\n`except ValueError:` pour capturer un type\nd'exception spécifique ; `else:` pour le\ncas où aucune exception n'a été levée ;\n`finally:` pour du code toujours exécuté.\nBonne pratique : capturer un type\nd'exception précis plutôt qu'utiliser un\n`except` nu."
    },
    {
      "id": "q03",
      "difficulty": 1,
      "skills": [
        "debugger"
      ],
      "title": "Débogueur Python",
      "statement": "Quel module de la bibliothèque standard de\nPython permet de déboguer interactivement ?",
      "options": [
        {
          "text": "`fix`",
          "correct": false,
          "feedback": "Aucun module nommé `fix` n'existe en\nPython.\n"
        },
        {
          "text": "`error`",
          "correct": false,
          "feedback": "Aucun module standard nommé `error`\nn'existe en Python.\n"
        },
        {
          "text": "`pdb`",
          "correct": true,
          "feedback": "On peut écrire `import pdb;\npdb.set_trace()`, ou plus simplement\n`breakpoint()` depuis Python 3.7,\npour poser un point d'arrêt.\nQuelques commandes utiles : `n`\n(passer à la ligne suivante), `s`\n(entrer dans la fonction), `c`\n(continuer), `p variable` (afficher\nune variable).\n"
        },
        {
          "text": "`debug`",
          "correct": false,
          "feedback": "Aucun module nommé `debug` n'existe\ndans la bibliothèque standard.\n"
        }
      ],
      "explanation": "Les environnements de développement\nmodernes (VS Code, PyCharm) intègrent des\ndébogueurs graphiques. Un simple clic\ndans la marge pose un point d'arrêt, et\nun panneau dédié affiche la valeur des\nvariables. C'est plus ergonomique que la\nligne de commande de `pdb`."
    },
    {
      "id": "q04",
      "difficulty": 1,
      "skills": [
        "logging"
      ],
      "title": "Journalisation",
      "statement": "Pourquoi préfère-t-on le module `logging` à\n`print` pour le débogage en production ?",
      "options": [
        {
          "text": "Pour filtrer les messages selon leur niveau de gravité",
          "correct": true,
          "feedback": "Le module `logging` est conçu pour la\nproduction. Il offre une\nconfiguration centralisée, plusieurs\nniveaux de gravité, un formatage\npersonnalisable et des destinataires\nvariés.\n"
        },
        {
          "text": "Parce que la fonction `print` n'existe pas du tout en Python",
          "correct": false,
          "feedback": "La fonction `print` existe bel et\nbien et reste utile, notamment pour\nl'apprentissage. Elle est simplement\ninsuffisante pour gérer les\nmessages de débogage en production.\n"
        },
        {
          "text": "Pour économiser de l'espace de stockage",
          "correct": false,
          "feedback": "L'économie d'espace n'est pas\nl'objectif principal. L'avantage du\nmodule `logging` se situe ailleurs,\ndans la flexibilité et la\nconfigurabilité des messages.\n"
        },
        {
          "text": "Aucun avantage particulier",
          "correct": false,
          "feedback": "Le module `logging` apporte une\nsouplesse importante par rapport à\nun simple `print`, comme détaillé\ndans la bonne réponse.\n"
        }
      ],
      "explanation": "Bonne pratique : utiliser le niveau de\ngravité approprié. Le niveau `debug`\nsert pour les détails de développement,\n`info` pour les événements normaux,\n`warning` pour les anomalies non\nbloquantes, `error` pour les erreurs\nrécupérables, et `critical` pour les\nerreurs fatales."
    },
    {
      "id": "q06",
      "difficulty": 1,
      "skills": [
        "test-integration"
      ],
      "title": "Test d'intégration",
      "statement": "Qu'est-ce qu'un **test d'intégration** ?",
      "options": [
        {
          "text": "Un test gratuit, sans contrepartie",
          "correct": false,
          "feedback": "Cette interprétation littérale du mot\n« intégration » n'a pas de sens en\ninformatique.\n"
        },
        {
          "text": "Un test qui vérifie l'intégrité complète de la pile d'exécution",
          "correct": false,
          "feedback": "Cette description ne correspond à\naucun test classique. Un test\nd'intégration porte sur les\ninteractions entre plusieurs modules.\n"
        },
        {
          "text": "Un test qui ne déclenche jamais d'erreur",
          "correct": false,
          "feedback": "Au contraire, un bon test\nd'intégration peut très bien\ndétecter des erreurs ; c'est même\nson but principal.\n"
        },
        {
          "text": "Un test vérifiant que plusieurs modules marchent ensemble",
          "correct": true,
          "feedback": "Ce type de test se distingue du test\nunitaire, qui ne vérifie qu'un seul\nmodule pris isolément. Le test\nd'intégration permet de détecter les\nbugs liés aux interactions, comme un\nmauvais format de données échangées\nentre deux modules.\n"
        }
      ],
      "explanation": "La pyramide classique des tests comporte\nbeaucoup de tests unitaires (rapides à\nexécuter), peu de tests d'intégration\n(plus lents mais essentiels), et très\npeu de tests de bout en bout (sur\nl'ensemble du système). C'est une\nstratégie pragmatique de couverture."
    },
    {
      "id": "q07",
      "difficulty": 1,
      "skills": [
        "stack-trace"
      ],
      "title": "Trace d'erreur",
      "statement": "Que représente la **trace d'erreur**\naffichée par Python à la suite d'une\nexception non capturée ?",
      "options": [
        {
          "text": "Un fichier journal stocké sur le disque",
          "correct": false,
          "feedback": "Une trace d'erreur n'est pas\nstockée par défaut sur le disque ;\nelle est simplement affichée dans\nla sortie d'erreur du programme.\n"
        },
        {
          "text": "Le code source complet du programme",
          "correct": false,
          "feedback": "La trace ne contient pas tout le\ncode source, mais uniquement la\nséquence d'appels qui a mené à\nl'erreur, avec les numéros de\nlignes correspondants.\n"
        },
        {
          "text": "La suite des appels de fonctions menant à l'erreur",
          "correct": true,
          "feedback": "Cette trace se lit du bas (lieu où\nl'erreur s'est produite) vers le\nhaut (point d'entrée du programme).\nElle aide à comprendre le contexte\ndans lequel l'erreur est apparue.\n"
        },
        {
          "text": "Un type de mémoire interne au système",
          "correct": false,
          "feedback": "Cette description évoque la pile\nd'appels en mémoire, qui est une\nnotion proche mais distincte. Ici,\non parle de l'affichage textuel\nobtenu à la sortie du programme.\n"
        }
      ],
      "explanation": "Pour lire une trace d'erreur, on\ncommence par la dernière ligne, qui\nindique le type d'erreur, puis on\nremonte pour localiser l'origine de\nl'appel problématique. On ignore\ngénéralement les blocs internes aux\nbibliothèques pour se concentrer sur\nson propre code."
    },
    {
      "id": "q08",
      "difficulty": 1,
      "skills": [
        "breakpoint"
      ],
      "title": "Point d'arrêt",
      "statement": "À quoi sert un **point d'arrêt** dans un\ndébogueur ?",
      "options": [
        {
          "text": "À empêcher d'écrire du code dans une certaine zone",
          "correct": false,
          "feedback": "Aucun point d'arrêt ne restreint\nl'écriture du code. Il agit\nuniquement à l'exécution.\n"
        },
        {
          "text": "À supprimer définitivement le programme",
          "correct": false,
          "feedback": "Un point d'arrêt n'a aucun effet\ndestructeur. Il ne fait que\nsuspendre temporairement\nl'exécution.\n"
        },
        {
          "text": "À suspendre l'exécution pour inspecter les variables",
          "correct": true,
          "feedback": "C'est une technique essentielle\npour comprendre un programme\ncomplexe. On peut placer plusieurs\npoints d'arrêt, voire des points\nconditionnels (« suspendre\nuniquement si $x > 100$ »).\n"
        },
        {
          "text": "À enregistrer automatiquement le fichier du programme",
          "correct": false,
          "feedback": "L'enregistrement du fichier source\nn'a aucun rapport avec un point\nd'arrêt.\n"
        }
      ],
      "explanation": "En Python, on peut écrire `breakpoint()`\ndans le code, ou cliquer dans la marge\nd'un environnement de développement\npour poser un point d'arrêt graphique.\nL'exécution s'arrête alors et l'on\npeut explorer l'état du programme\ninteractivement."
    },
    {
      "id": "q09",
      "difficulty": 1,
      "skills": [
        "reproduction"
      ],
      "title": "Reproduction d'un bug",
      "statement": "Pourquoi est-il **essentiel** de pouvoir\nreproduire un bug avant de le corriger ?",
      "options": [
        {
          "text": "Pour pouvoir le montrer à l'utilisateur final",
          "correct": false,
          "feedback": "La présentation du bug à\nl'utilisateur n'est pas la raison\npour laquelle on cherche à le\nreproduire. L'objectif est de\npouvoir vérifier qu'il a été\ncorrigé.\n"
        },
        {
          "text": "Sans reproduction, impossible de vérifier la correction",
          "correct": true,
          "feedback": "Un bug intermittent est très\ndifficile à corriger. Trouver une\nprocédure fiable de reproduction\nreprésente déjà la moitié du\ntravail de correction.\n"
        },
        {
          "text": "Pour s'amuser à le voir se produire",
          "correct": false,
          "feedback": "La reproduction d'un bug est un\ntravail technique sérieux, motivé\npar la nécessité de pouvoir\ncontrôler la correction.\n"
        },
        {
          "text": "Aucune raison particulière",
          "correct": false,
          "feedback": "La reproduction est au contraire\nl'étape fondatrice de tout\nprocessus de correction sérieux.\n"
        }
      ],
      "explanation": "L'adage classique en débogage est :\n« si l'on ne peut pas reproduire le\nbug, on ne peut pas le corriger ».\nUne fois la reproduction obtenue, on\nécrit un test qui échoue dans cette\nsituation, puis on corrige jusqu'à ce\nque ce test passe avec succès."
    },
    {
      "id": "q10",
      "difficulty": 1,
      "skills": [
        "methode-systematique"
      ],
      "title": "Réduction au cas minimal",
      "statement": "Quelle est l'une des techniques\nsystématiques pour **isoler la cause**\nd'un bug dans un programme ?",
      "options": [
        {
          "text": "Réduire l'entrée au cas le plus simple qui reproduit encore le bug",
          "correct": true,
          "feedback": "Cette technique du « cas de test\nminimal » est très efficace : si\nun programme plante sur une liste\nde mille éléments, on essaie avec\ndix, puis cinq, puis deux, en\ngardant à chaque fois la\npropriété qui déclenche le bug.\nCela localise la cause et fournit\ngratuitement un test de\nrégression compact.\n"
        },
        {
          "text": "Multiplier la taille de l'entrée jusqu'à observer un comportement régulier",
          "correct": false,
          "feedback": "C'est l'inverse. Plus l'entrée est\ngrande, plus il est difficile\nd'identifier précisément ce qui\ndéclenche le bug.\n"
        },
        {
          "text": "Demander conseil à un collègue",
          "correct": false,
          "feedback": "Cela peut aider à clarifier ses\nidées (technique dite « du canard\nen plastique »), mais n'est pas\nune méthode systématique\nd'isolation de la cause.\n"
        },
        {
          "text": "Tout réécrire de zéro",
          "correct": false,
          "feedback": "Cette démarche est risquée : elle\npeut introduire de nouveaux bugs\net masquer l'origine du problème\ninitial.\n"
        }
      ],
      "explanation": "Réduction au cas minimal et\nexplication verbale (du code à un\ncollègue, ou même à un objet) sont\nles deux méthodes systématiques\nutiles à connaître. Elles\ncomplètent les outils techniques\n(débogueur, journaux, profilage)."
    },
    {
      "id": "q11",
      "difficulty": 2,
      "skills": [
        "raise-exception"
      ],
      "title": "Lever une exception explicitement",
      "statement": "Dans une fonction `racine_carree(x)`, on\nsouhaite **signaler une erreur** lorsque\n`x` est négatif. Quelle est la bonne\npratique ?",
      "options": [
        {
          "text": "Lever explicitement une exception avec `raise`",
          "correct": true,
          "feedback": "Bonne réponse, par exemple :\n\n```python\nif x < 0:\n    raise ValueError(\"x doit être positif\")\n```\n\nL'instruction `raise` interrompt\nimmédiatement l'exécution et\nsignale un problème de manière\nexplicite. L'appelant peut\ncapturer cette exception avec\n`try / except` s'il le souhaite,\net le message rend le bug facile à\ncomprendre.\n"
        },
        {
          "text": "Renvoyer la valeur $-1$ pour signaler l'erreur",
          "correct": false,
          "feedback": "Cette pratique est dangereuse :\nl'appelant peut oublier de tester\nla valeur de retour, et la valeur\n$-1$ peut aussi être un résultat\nlégitime pour une autre fonction.\n"
        },
        {
          "text": "Afficher un message avec `print` puis renvoyer la valeur $0$",
          "correct": false,
          "feedback": "L'appel à `print` ne peut pas être\ncapturé par l'appelant, et la\nvaleur $0$ ne se distingue pas\nd'un résultat valide. Cette\napproche dissimule donc le\nproblème.\n"
        },
        {
          "text": "Continuer le calcul comme si de rien n'était",
          "correct": false,
          "feedback": "Cette pratique conduit à un\nrésultat numérique faux (le plus\nsouvent `nan` ou une autre valeur\naberrante), ce qui rend le bug\ntrès difficile à diagnostiquer\nensuite.\n"
        }
      ],
      "explanation": "Les instructions `raise ValueError(...)`\nou `raise TypeError(...)` sont les\nmoyens standard de signaler une\nmauvaise utilisation d'une fonction.\nCombinées à `try / except`, elles\noffrent une gestion robuste des\nerreurs sans imposer une convention\nde retour fragile à l'appelant."
    },
    {
      "id": "q12",
      "difficulty": 2,
      "skills": [
        "raise"
      ],
      "title": "L'instruction raise",
      "statement": "Dans quelles situations utilise-t-on\nl'instruction `raise` en Python ?",
      "options": [
        {
          "text": "Pour lever une exception et signaler une erreur",
          "correct": true,
          "feedback": "On l'utilise par exemple pour\nvalider un paramètre :\n`if x < 0: raise ValueError(\"x\ndoit être positif\")`. C'est une\napproche défensive de la\nprogrammation.\n"
        },
        {
          "text": "Pour multiplier la valeur d'une variable par deux",
          "correct": false,
          "feedback": "La multiplication d'une variable\ns'écrit avec l'opérateur `*`,\ncomme dans `x = x * 2`. Aucun\nrapport avec l'instruction\n`raise`.\n"
        },
        {
          "text": "Pour incrémenter un compteur",
          "correct": false,
          "feedback": "L'incrémentation d'un compteur\ns'écrit `c = c + 1` ou `c += 1`,\net n'a aucun rapport avec\nl'instruction `raise`.\n"
        },
        {
          "text": "Pour quitter un programme",
          "correct": false,
          "feedback": "Pour quitter un programme, on\nutilise plutôt `sys.exit()`. Une\nexception non capturée arrête\naussi l'exécution, mais ce n'est\npas l'usage premier de `raise`.\n"
        }
      ],
      "explanation": "L'avantage par rapport à un simple\n`if` qui renverrait `None` : l'erreur\nne peut pas être ignorée\nsilencieusement, puisque le\nprogramme s'arrête sauf si l'on\ncapture l'exception explicitement\navec `try / except`."
    },
    {
      "id": "q13",
      "difficulty": 2,
      "skills": [
        "piege-except-nu"
      ],
      "title": "Le bloc except sans type",
      "statement": "Pourquoi le bloc `except:` sans\nprécision de type est-il **déconseillé** ?",
      "options": [
        {
          "text": "Il capture toutes les exceptions, même les interruptions clavier",
          "correct": true,
          "feedback": "Capturer indistinctement toutes\nles exceptions est une mauvaise\npratique. Mieux vaut écrire\n`except Exception:` (qui\nn'attrape pas les interruptions\nclavier) ou, idéalement, capturer\nun type d'exception spécifique.\n"
        },
        {
          "text": "Parce qu'il est plus lent à l'exécution",
          "correct": false,
          "feedback": "La performance n'est pas le\ncritère ici. Le problème est\nd'ordre fonctionnel : un\n`except` nu intercepte trop de\nchoses.\n"
        },
        {
          "text": "Aucune raison ne justifie de l'éviter",
          "correct": false,
          "feedback": "Au contraire, plusieurs raisons\njustifient d'éviter le `except`\nnu, comme expliqué dans la bonne\nréponse.\n"
        },
        {
          "text": "Parce que la syntaxe `except:` n'existe tout simplement pas en Python",
          "correct": false,
          "feedback": "La syntaxe existe et est\nsyntaxiquement valide. Le\nproblème est sémantique : elle\ncapture trop largement.\n"
        }
      ],
      "explanation": "Règle d'or : ne jamais ignorer\nsilencieusement une erreur. Au\nminimum, il faut journaliser ce qui\ns'est passé, même si l'on choisit\nde poursuivre l'exécution."
    },
    {
      "id": "q14",
      "difficulty": 2,
      "skills": [
        "finally"
      ],
      "title": "Le bloc finally",
      "statement": "À quoi sert le bloc `finally` dans une\nstructure `try / except` ?",
      "options": [
        {
          "text": "À exécuter du code dans tous les cas (nettoyage)",
          "correct": true,
          "feedback": "Exemple typique :\n`try: f = open(...)` suivi de\n`finally: f.close()`. Cette\nconstruction garantit la\nfermeture du fichier, même en\ncas d'erreur. La construction\nplus moderne `with` (gestionnaire\nde contexte) automatise ce type\nde gestion.\n"
        },
        {
          "text": "À ralentir intentionnellement l'exécution",
          "correct": false,
          "feedback": "Le bloc `finally` n'a aucun\nobjectif de ralentissement. Son\nrôle est purement fonctionnel.\n"
        },
        {
          "text": "À empêcher la levée des exceptions",
          "correct": false,
          "feedback": "Le bloc `finally` ne supprime pas\nles exceptions. Il garantit\nsimplement l'exécution de\ncertaines instructions, quoi\nqu'il advienne.\n"
        },
        {
          "text": "À exécuter du code uniquement lorsqu'une erreur survient",
          "correct": false,
          "feedback": "Cette description correspond au\nbloc `except`. Le bloc `finally`\na un rôle différent : il\ns'exécute dans tous les cas.\n"
        }
      ],
      "explanation": "Le motif moderne consiste à utiliser\n`with open(f) as fichier:`, qui\nferme automatiquement le fichier,\nmême en cas d'erreur. Cette syntaxe\nest plus concise que la construction\n`try / finally`."
    },
    {
      "id": "q15",
      "difficulty": 2,
      "skills": [
        "profile-cprofile"
      ],
      "title": "Mesurer le temps d'exécution",
      "statement": "Comment **mesurer** où un script Python\npasse le plus de temps ?",
      "options": [
        {
          "text": "Avec un chronomètre",
          "correct": false,
          "feedback": "Cette mesure est trop manuelle\net ne permet pas d'identifier\nfinement les fonctions les plus\ncoûteuses dans le code.\n"
        },
        {
          "text": "En le lançant plusieurs fois et en chronométrant",
          "correct": false,
          "feedback": "Cette mesure globale du temps\ntotal ne dit pas **où**, dans le\ncode, le programme passe son\ntemps. Or c'est précisément\nl'information nécessaire pour\noptimiser.\n"
        },
        {
          "text": "Il est impossible de mesurer cela en Python",
          "correct": false,
          "feedback": "Plusieurs outils existent en\nPython pour mesurer la\nperformance d'un script,\nnotamment `cProfile`,\n`line_profiler` ou `timeit`.\n"
        },
        {
          "text": "Avec la commande `python -m cProfile -s time script.py`",
          "correct": true,
          "feedback": "Cette commande standard affiche\nles fonctions triées par temps\ncumulé d'exécution, ce qui\npermet d'identifier rapidement\nles parties les plus coûteuses\ndu code.\n"
        }
      ],
      "explanation": "Pour mesurer la consommation\nmémoire, on utilise `memory_profiler`.\nPour analyser ligne par ligne le\ntemps passé, on utilise\n`line_profiler`. Ces deux\nbibliothèques s'installent\nséparément, par exemple avec\n`pip install`."
    },
    {
      "id": "q16",
      "difficulty": 2,
      "skills": [
        "test-regression"
      ],
      "title": "Test de régression",
      "statement": "Qu'est-ce qu'un **test de régression** ?",
      "options": [
        {
          "text": "Un test qui empêche un bug corrigé de réapparaître plus tard",
          "correct": true,
          "feedback": "Cette pratique est essentielle\npour éviter qu'un bug déjà\ncorrigé ne réapparaisse à\nl'occasion de modifications\nultérieures. Une discipline\ncourante consiste à ajouter un\ntest pour chaque bug corrigé.\n"
        },
        {
          "text": "Un test trop simple pour être utile",
          "correct": false,
          "feedback": "La simplicité d'un test n'a rien\nà voir avec sa qualité de test\nde régression. Beaucoup de tests\nde régression sont d'ailleurs\ntrès simples par construction.\n"
        },
        {
          "text": "Un test exécuté sur l'ancienne version du code",
          "correct": false,
          "feedback": "Un test de régression s'exécute\nsur la **version courante** du\ncode, et non sur une ancienne\nversion. Son rôle est de\nvérifier qu'un bug ancien ne\nréapparaît pas.\n"
        },
        {
          "text": "Un test qui revient en arrière dans le code source",
          "correct": false,
          "feedback": "Cette interprétation littérale du\nmot « régression » est inexacte.\nLe test de régression vise à\nempêcher la réapparition d'un\nbug, pas à revenir en arrière\ndans le code.\n"
        }
      ],
      "explanation": "Un cycle classique de correction de\nbug consiste à : écrire d'abord un\ntest qui reproduit le bug et échoue,\npuis corriger le code jusqu'à ce\nque ce test passe avec succès, puis\nenfin nettoyer la structure du code."
    },
    {
      "id": "q18",
      "difficulty": 2,
      "skills": [
        "trace-execution"
      ],
      "title": "Trace d'exécution",
      "statement": "Que produit une **trace d'exécution** d'un\nprogramme ?",
      "options": [
        {
          "text": "Une optimisation du code",
          "correct": false,
          "feedback": "La trace est un outil\nd'observation du comportement,\net non une transformation du\ncode visant à le rendre plus\nrapide.\n"
        },
        {
          "text": "Un journal des étapes réellement exécutées",
          "correct": true,
          "feedback": "La trace d'exécution permet de\ncomprendre le déroulement réel\ndu programme, ce qui est\nparticulièrement utile pour des\nbugs complexes ou intermittents.\nElle peut être réalisée avec\n`print`, `logging`, ou des\ndécorateurs dédiés.\n"
        },
        {
          "text": "Une erreur d'exécution",
          "correct": false,
          "feedback": "La trace d'exécution n'est pas\nun message d'erreur, mais le\njournal complet du déroulement\ndu programme.\n"
        },
        {
          "text": "Une simple copie intégrale du code source du programme",
          "correct": false,
          "feedback": "La trace d'exécution n'est pas\nle code source du programme,\nmais un journal de son\ncomportement effectif.\n"
        }
      ],
      "explanation": "Le module `trace` de Python permet\nd'obtenir une trace très complète,\navec la commande\n`python -m trace --trace script.py`.\nLe résultat est verbeux mais utile\npour analyser des comportements\ncomplexes."
    },
    {
      "id": "q19",
      "difficulty": 2,
      "skills": [
        "assertion"
      ],
      "title": "Assertion et exception",
      "statement": "Quelle est la différence entre une\n**assertion** et la gestion d'exception ?",
      "options": [
        {
          "text": "Aucune différence",
          "correct": false,
          "feedback": "Les deux mécanismes sont\ndistincts, à la fois par leur\nfonction et par leur usage,\ncomme détaillé dans la bonne\nréponse.\n"
        },
        {
          "text": "Les assertions s'exécutent toujours bien plus rapidement que les exceptions",
          "correct": false,
          "feedback": "La performance n'est pas le\ncritère pertinent ici. Les deux\nmécanismes ont des rôles\ndifférents.\n"
        },
        {
          "text": "Les exceptions n'existent pas en Python",
          "correct": false,
          "feedback": "Les exceptions sont au contraire\nun mécanisme central du langage\nPython.\n"
        },
        {
          "text": "Les assertions signalent un bug, les exceptions gèrent des cas attendus",
          "correct": true,
          "feedback": "On peut résumer ainsi : `assert`\nexprime « cela ne doit jamais\narriver », tandis que\n`raise` combiné à `try / except`\nexprime « cela peut arriver, et\nvoici comment je le gère ».\n"
        }
      ],
      "explanation": "Une assertion est désactivable avec\nl'option `python -O` (typiquement\nen production). Il faut donc éviter\nd'utiliser `assert` pour des\nvérifications critiques portant sur\ndes entrées utilisateur. Pour\ncelles-ci, il vaut mieux utiliser\n`raise`."
    },
    {
      "id": "q20",
      "difficulty": 2,
      "skills": [
        "outils-ide"
      ],
      "title": "Outils d'environnement de développement",
      "statement": "Quels **outils** d'un environnement de\ndéveloppement intégré facilitent le\ndébogage ?",
      "options": [
        {
          "text": "Inspecteur de variables, pas à pas et points d'arrêt",
          "correct": true,
          "feedback": "Les environnements modernes\ncomme VS Code, PyCharm ou\nJupyter offrent tous ces\noutils. Un environnement bien\nmaîtrisé est un accélérateur\nmajeur du travail de débogage.\n"
        },
        {
          "text": "Aucun outil particulier n'aide au débogage",
          "correct": false,
          "feedback": "Au contraire, les environnements\nmodernes proposent une riche\npalette d'outils dédiés au\ndébogage.\n"
        },
        {
          "text": "Uniquement la coloration syntaxique",
          "correct": false,
          "feedback": "La coloration syntaxique est\nutile, mais loin d'être\nsuffisante. Les véritables\noutils de débogage vont\nbeaucoup plus loin.\n"
        },
        {
          "text": "Le simple choix d'un clavier en disposition AZERTY ou QWERTY",
          "correct": false,
          "feedback": "La disposition du clavier n'a\naucun rapport avec les outils de\ndébogage de l'environnement de\ndéveloppement.\n"
        }
      ],
      "explanation": "Un investissement utile consiste à\napprendre les raccourcis clavier\nde son environnement de\ndéveloppement. Les raccourcis\nclassiques sont F5 pour exécuter\nou déboguer, F10 pour passer à la\nligne suivante, F11 pour entrer\ndans une fonction, et F9 pour\nposer ou retirer un point d'arrêt."
    },
    {
      "id": "q21",
      "difficulty": 3,
      "skills": [
        "bug-difficile"
      ],
      "title": "Bug fantôme",
      "statement": "Qu'appelle-t-on un **bug fantôme** ou\n*Heisenbug*, en référence au principe\nd'incertitude de Heisenberg ?",
      "options": [
        {
          "text": "Un bug provenant d'un programme allemand",
          "correct": false,
          "feedback": "La référence à Heisenberg\nconcerne le physicien allemand\ndu XXe siècle, mais le bug\nlui-même n'a aucune origine\ngéographique particulière.\n"
        },
        {
          "text": "Un bug visible uniquement la nuit",
          "correct": false,
          "feedback": "Cette interprétation est\nfantaisiste. Le terme\n« fantôme » fait référence à\nl'évanescence du bug, pas à\nun horaire d'apparition.\n"
        },
        {
          "text": "Un bug qui change dès qu'on tente de l'observer",
          "correct": true,
          "feedback": "Le nom fait référence au\nprincipe d'incertitude\nd'Heisenberg : observer le\nphénomène le modifie. Ces bugs\nsont particulièrement\ndifficiles à diagnostiquer.\nCauses typiques : interactions\nentre fils d'exécution\nconcurrents, mémoire partagée,\ndépendances temporelles.\n"
        },
        {
          "text": "Un bug que l'on ne parvient absolument jamais à reproduire",
          "correct": false,
          "feedback": "Cette description est incomplète.\nLa caractéristique essentielle\nest plus précise : le bug\ndisparaît ou change de\ncomportement quand on essaie de\nl'observer.\n"
        }
      ],
      "explanation": "Une stratégie utile consiste à\najouter du logging systématique\nplutôt que des modifications\nponctuelles. Si le bug disparaît\navec l'ajout du logging, c'est un\nindice fort qu'il est lié à des\nquestions de synchronisation\ntemporelle."
    },
    {
      "id": "q23",
      "difficulty": 3,
      "skills": [
        "analyse-journaux"
      ],
      "title": "Analyse d'un journal d'exécution",
      "statement": "Face à un programme qui écrit\nrégulièrement dans un journal\nd'exécution, quelle stratégie\nd'analyse est la plus efficace\nlorsqu'un bug est signalé en\nproduction ?",
      "options": [
        {
          "text": "Lire l'intégralité du journal depuis le début, ligne par ligne",
          "correct": false,
          "feedback": "Démarche peu efficace : un\njournal de production peut\ncontenir des millions de lignes.\nIl faut cibler la zone\ntemporelle proche du moment\noù le bug s'est produit.\n"
        },
        {
          "text": "Effacer le journal et redémarrer le programme",
          "correct": false,
          "feedback": "Démarche désastreuse : on\nperd la principale source\nd'information sur le bug. Au\ncontraire, il faut conserver\nprécieusement les journaux\ndes incidents pour pouvoir\nles analyser à froid.\n"
        },
        {
          "text": "Désactiver le journal et compter sur les utilisateurs pour signaler les erreurs",
          "correct": false,
          "feedback": "Démarche contre-productive : un\njournal détaillé est l'outil\nprincipal de diagnostic en\nproduction. Sans lui, on est\naveugle quand un bug\nsurvient.\n"
        },
        {
          "text": "Cibler la fenêtre temporelle de l'erreur, puis remonter à l'événement anormal",
          "correct": true,
          "feedback": "C'est la démarche standard.\nOn part de la trace d'erreur\n(qui donne l'horodatage et le\ncontexte), on filtre les\nlignes voisines (avec `grep`,\n`tail`, ou un outil dédié),\npuis on remonte la chaîne\nde causalité. Le journal\nrévèle alors ce qui a\nprécédé le plantage : appel\ninattendu, état incohérent,\nressource manquante, etc.\n"
        }
      ],
      "explanation": "Bonne pratique : structurer ses\njournaux avec des niveaux\n(`debug`, `info`, `warning`,\n`error`, `critical`) et des\nhorodatages. En cas d'incident,\nfiltrer le journal par niveau\net par fenêtre temporelle\npermet de reconstituer\nrapidement la chaîne d'événements\nqui a mené au bug."
    },
    {
      "id": "q24",
      "difficulty": 3,
      "skills": [
        "synthese-systematique"
      ],
      "title": "Méthodologie face à un bug",
      "statement": "Quelle est la **bonne méthodologie**\ngénérale lorsqu'on est confronté à un\nbug ?",
      "options": [
        {
          "text": "Réécrire l'intégralité du programme",
          "correct": false,
          "feedback": "Réécrire l'ensemble du\nprogramme est rarement la\nbonne réponse. Cela demande un\neffort considérable et risque\nd'introduire d'autres bugs.\n"
        },
        {
          "text": "Demander la solution à un collègue",
          "correct": false,
          "feedback": "Solliciter un collègue peut\nêtre utile dans le diagnostic,\nmais ne constitue pas une\nméthodologie complète face à un\nbug.\n"
        },
        {
          "text": "Modifier le code au hasard jusqu'à ce que le bug finisse par disparaître",
          "correct": false,
          "feedback": "Cette démarche est dangereuse :\nelle risque d'introduire de\nnouveaux bugs et masque\nl'origine réelle du problème.\n"
        },
        {
          "text": "Reproduire, isoler, corriger, puis ajouter un test de régression",
          "correct": true,
          "feedback": "Cette méthodologie est éprouvée.\nSauter une étape, en particulier\nl'ajout du test de régression,\ngarantit que le bug finira par\nréapparaître.\n"
        }
      ],
      "explanation": "La discipline est essentielle. Il\nfaut s'imposer de ne jamais\ncorriger un bug sans avoir réussi\nà le reproduire, et de ne jamais\ncorriger sans ajouter ensuite un\ntest. Sinon, le bug finira par\nréapparaître, et le temps gagné\nau départ sera perdu plus tard."
    },
    {
      "id": "q25",
      "difficulty": 3,
      "skills": [
        "synthese"
      ],
      "title": "Synthèse",
      "statement": "Parmi les affirmations suivantes sur la\nmise au point des programmes, laquelle\nest **fausse** ?",
      "options": [
        {
          "text": "Il faut corriger le bug d'abord, puis essayer de le reproduire",
          "correct": true,
          "feedback": "C'est l'inverse qui est vrai\n(donc cette affirmation est\nbien la fausse). Il faut\nd'abord **reproduire** le bug,\nafin de pouvoir vérifier\nensuite que la correction est\neffective, puis seulement\ncorriger.\n"
        },
        {
          "text": "La construction `try / except` permet de capturer des exceptions",
          "correct": false,
          "feedback": "Cette affirmation est correcte.\nC'est précisément le rôle du\nmécanisme `try / except`.\n"
        },
        {
          "text": "Le profilage indique où, dans le code, le programme passe son temps",
          "correct": false,
          "feedback": "Cette affirmation est correcte.\nC'est l'un des principaux\nobjectifs du profilage.\n"
        },
        {
          "text": "Une assertion qui échoue signale typiquement un bug dans le programme",
          "correct": false,
          "feedback": "Cette affirmation est correcte.\nUne assertion représente une\nvérification d'invariant : si\nelle échoue, c'est qu'une\nhypothèse fondamentale du\nprogramme est violée.\n"
        }
      ],
      "explanation": "L'adage classique en débogage est :\n« si l'on ne peut pas reproduire le\nbug, on ne peut pas le corriger ».\nLa reproduction est l'étape\npremière, celle qui permet de\nmesurer la qualité de la\ncorrection."
    },
    {
      "id": "q26",
      "difficulty": 2,
      "skills": [
        "gestionnaire-contexte"
      ],
      "title": "Le mot-clé with",
      "statement": "Quel est l'avantage d'écrire\n```python\nwith open(\"fichier.txt\") as f:\n    contenu = f.read()\n```\npar rapport à\n```python\nf = open(\"fichier.txt\")\ncontenu = f.read()\nf.close()\n```\n?",
      "options": [
        {
          "text": "La construction `with` permet\nd'écrire dans le fichier sans\nouvrir un mode d'écriture\n",
          "correct": false,
          "feedback": "Erreur : `with` n'a aucun effet\nsur le mode d'ouverture. Pour\nécrire dans un fichier, il faut\ntoujours préciser `mode='w'` ou\n`'a'`, que l'on utilise `with` ou\nnon.\n"
        },
        {
          "text": "La construction `with` est\nobligatoire en Python depuis la\nversion 3\n",
          "correct": false,
          "feedback": "Erreur : la construction `with`\nn'est pas obligatoire ; elle est\nfortement recommandée. La\nversion manuelle avec `open` et\n`close` reste valide\nsyntaxiquement, mais elle est\nfragile face aux exceptions.\n"
        },
        {
          "text": "La construction `with` est plus\nrapide à l'exécution\n",
          "correct": false,
          "feedback": "Erreur : la performance est\nidentique. L'avantage est dans la\nrobustesse face aux exceptions, pas\ndans la vitesse.\n"
        },
        {
          "text": "La construction `with` ferme automatiquement le fichier, même en cas d'exception",
          "correct": true,
          "feedback": "Bonne réponse : c'est la principale\nraison d'utiliser un gestionnaire\nde contexte. Si une exception est\nlevée pendant le `read()` dans la\nversion sans `with`, l'instruction\n`f.close()` ne sera jamais\nexécutée, et le descripteur de\nfichier reste ouvert. La construction\n`with` garantit la fermeture, qu'il\ny ait erreur ou non. Équivaut à un\n`try / finally` propre.\n"
        }
      ],
      "explanation": "Le motif `with` est en réalité un\n**gestionnaire de contexte** (*context\nmanager*). Tout objet implémentant\nles méthodes `__enter__` et `__exit__`\npeut servir de contexte. C'est utile\npour les fichiers, les connexions\nréseau, les verrous, les\ntransactions de base de données."
    }
  ]
}