{
  "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 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 durant l'exécution, qui interrompt le flux normal du programme sauf si elle est capturée",
          "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\ncatch risque() {}\n```\n",
          "correct": false,
          "feedback": "Cette syntaxe est utilisée en Java ou\nen C++, mais pas en Python, qui\nemploie `try` et `except`.\n"
        },
        {
          "text": "```python\nif risque() == error: ...\n```\n",
          "correct": false,
          "feedback": "Cette syntaxe n'est pas valide en\nPython. 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 pouvoir activer ou désactiver les messages, choisir leur niveau de gravité (`debug`, `info`, `warning`, `error`, `critical`) et les rediriger vers un fichier",
          "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 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": "q05",
      "difficulty": 1,
      "skills": [
        "profiler"
      ],
      "title": "Profilage",
      "statement": "Que désigne-t-on par le **profilage** d'un\nprogramme ?",
      "options": [
        {
          "text": "Établir une description de son auteur",
          "correct": false,
          "feedback": "Le profilage en informatique n'a\naucun rapport avec une description\nbiographique. Il s'agit d'une mesure\nde performances.\n"
        },
        {
          "text": "Mesurer où le programme passe son temps et consomme la mémoire, afin d'identifier les parties les plus coûteuses",
          "correct": true,
          "feedback": "Avant d'optimiser, il faut mesurer.\nUn adage célèbre rappelle que\n« l'optimisation prématurée est la\nracine de tous les maux ». Quelques\noutils Python : `cProfile`,\n`line_profiler`, `memory_profiler`.\n"
        },
        {
          "text": "Compresser le code pour gagner de la place",
          "correct": false,
          "feedback": "La compression du code n'a aucun\nrapport avec le profilage, qui\nmesure le temps d'exécution et la\nconsommation mémoire.\n"
        },
        {
          "text": "Lancer le programme plusieurs fois pour vérifier sa stabilité",
          "correct": false,
          "feedback": "Lancer un programme plusieurs fois\nrelève plutôt du test de robustesse.\nLe profilage cherche à localiser les\nparties coûteuses, et non à mesurer\nla stabilité globale.\n"
        }
      ],
      "explanation": "Avec `cProfile`, la commande\n`python -m cProfile -s time script.py`\naffiche les fonctions triées par temps\ncumulé d'exécution. Cela permet\nd'identifier les $10\\%$ du code qui\nconsomment $90\\%$ du temps total."
    },
    {
      "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é 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 qui vérifie que plusieurs modules fonctionnent correctement ensemble, et pas seulement chacun isolément",
          "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 au moment où l'exception a été levée, chaque ligne indiquant la fonction appelante",
          "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 à une ligne précise pour inspecter les variables et progresser pas à pas",
          "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 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": "Parce qu'on ne peut pas vérifier qu'une correction est efficace si l'on n'arrive pas à redéclencher le bug",
          "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 progressivement l'entrée problématique au cas le plus simple qui reproduit encore le bug, ce qui localise efficacement la zone fautive",
          "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, par\nexemple avec :\n\n```python\nif x < 0:\n    raise ValueError(\"x doit être positif\")\n```\n",
          "correct": true,
          "feedback": "L'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 explicitement 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",
          "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": "Parce qu'il capture toutes les exceptions, y compris `KeyboardInterrupt` (interruption clavier) et `SystemExit`, ce qui peut masquer des bugs et empêcher l'arrêt propre du programme",
          "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 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, qu'il y ait eu une exception ou non. Il est utile pour les opérations de nettoyage (fermeture de fichiers, libération de ressources)",
          "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 en cas d'erreur",
          "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 ajouté après avoir corrigé un bug, afin de vérifier que ce bug ne réapparaisse pas lors de modifications ultérieures du code",
          "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": "q17",
      "difficulty": 2,
      "skills": [
        "methode-canard"
      ],
      "title": "Méthode du canard en plastique",
      "statement": "Que désigne la méthode dite **du canard\nen plastique** en débogage ?",
      "options": [
        {
          "text": "Une méthode de compression de code",
          "correct": false,
          "feedback": "Aucun rapport avec la\ncompression. Il s'agit d'une\nméthode psychologique de\ndébogage.\n"
        },
        {
          "text": "Un type particulier de test automatique",
          "correct": false,
          "feedback": "Cette méthode n'a rien à voir\navec un test automatique. Elle\nrepose sur l'explication\nverbale du code, généralement\noralement.\n"
        },
        {
          "text": "La technique consistant à expliquer son code à voix haute, à un objet ou à un interlocuteur, ce qui force à clarifier sa pensée et révèle souvent l'origine du bug",
          "correct": true,
          "feedback": "Cette technique est réelle et\nremarquablement efficace. Le fait\nd'expliquer le code structure le\nraisonnement et fait apparaître\nles hypothèses cachées qui\npeuvent être à l'origine du\nbug.\n"
        },
        {
          "text": "L'utilisation d'un canard en plastique magique pour résoudre les bugs",
          "correct": false,
          "feedback": "Cette interprétation est\nplaisante mais ne reflète qu'une\nmoitié de l'idée. Le sens réel\nest plus profond et concerne le\nprocessus mental d'explication.\n"
        }
      ],
      "explanation": "Plusieurs variantes existent : on\npeut expliquer son code à un\ncollègue, ou rédiger un message\ndétaillé qu'on n'envoie finalement\npas. De nombreux bugs se résolvent\nd'ailleurs au cours de la rédaction\nmême de l'explication."
    },
    {
      "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 effectivement exécutées par le programme, incluant les entrées et sorties de fonctions et les valeurs des variables clés",
          "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 du code source",
          "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 plus rapidement",
          "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 vérifient des invariants (des conditions qui ne devraient jamais être fausses) et signalent un bug. Les exceptions, elles, gèrent des conditions exceptionnelles attendues (entrée invalide, fichier manquant) et peuvent être capturées et traitées",
          "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": "L'inspecteur de variables, la navigation pas à pas, les points d'arrêt conditionnels, la console interactive et l'analyseur statique",
          "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 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 disparaît ou change lorsqu'on tente de l'observer (par exemple, en ajoutant des `print` ou en lançant le programme dans un débogueur). L'origine est souvent liée à des problèmes de synchronisation ou de mémoire non initialisée",
          "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 pas à 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": "q22",
      "difficulty": 3,
      "skills": [
        "memory-leak"
      ],
      "title": "Fuite de mémoire",
      "statement": "Qu'appelle-t-on une **fuite de mémoire** ?",
      "options": [
        {
          "text": "De l'eau qui s'infiltre dans l'ordinateur",
          "correct": false,
          "feedback": "Cette interprétation littérale\nest plaisante, mais sans rapport\navec le concept informatique.\nLe terme « fuite de mémoire »\ndécrit un comportement\nlogiciel.\n"
        },
        {
          "text": "Une optimisation du programme",
          "correct": false,
          "feedback": "Une fuite de mémoire est au\ncontraire un défaut, qui dégrade\nla performance et peut faire\nplanter le programme à terme.\n"
        },
        {
          "text": "Une situation dans laquelle le programme conserve des références à des données dont il n'a plus besoin, accumulant ainsi la mémoire utilisée jusqu'à un éventuel arrêt brutal",
          "correct": true,
          "feedback": "Ce phénomène est moins fréquent\nen Python, qui dispose d'un\nramasse-miettes, qu'en C ou en\nC++. Il peut néanmoins se\nproduire, par exemple via des\ncaches non bornés ou des\nréférences cycliques associées\nà des méthodes spéciales comme\n`__del__`.\n"
        },
        {
          "text": "La perte d'un fichier sur le disque",
          "correct": false,
          "feedback": "Une fuite de mémoire concerne\nla mémoire vive utilisée par\nun programme, et non un fichier\nsur le disque.\n"
        }
      ],
      "explanation": "Plusieurs outils permettent de\ndétecter les fuites de mémoire en\nPython : `tracemalloc` pour suivre\nles allocations, ou `objgraph`\npour visualiser les graphes de\nréférences. Ces outils sont\nparticulièrement utiles pour les\nprogrammes qui s'exécutent sur de\nlongues durées, comme les\nserveurs."
    },
    {
      "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": "Localiser dans le journal la fenêtre temporelle proche de l'erreur, puis remonter à rebours en cherchant le premier événement anormal qui a précédé l'erreur observée",
          "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 disparaisse",
          "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 le bug, isoler la cause\n(par réduction au cas minimal ou\navec un débogueur), corriger le\ncode, ajouter un test de\nrégression, puis documenter si\npertinent\n",
          "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": "La règle d'or consiste à corriger d'abord le bug, 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\nautomatiquement le fichier à\nla sortie du bloc, même si une\nexception est levée pendant la\nlecture, ce qui évite les fuites\nde descripteurs de fichier\n",
          "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."
    },
    {
      "id": "q27",
      "difficulty": 3,
      "skills": [
        "optimisation-prematuree"
      ],
      "title": "Optimisation prématurée",
      "statement": "Donald Knuth a écrit en 1974 :\n« **L'optimisation prématurée est la\nracine de tous les maux** ». Quelle est\nla signification pratique de cette\nmaxime pour le développement logiciel ?",
      "options": [
        {
          "text": "Il faut optimiser dès la première\nligne de code écrite\n",
          "correct": false,
          "feedback": "Erreur : c'est exactement ce que\nKnuth dénonce. Optimiser avant\nd'avoir un programme correct\nconduit souvent à du code complexe,\nbuggé, et qui n'apporte pas\nforcément le gain attendu.\n"
        },
        {
          "text": "Cette maxime ne s'applique qu'aux\nprogrammes anciens, pas aux\nprogrammes modernes\n",
          "correct": false,
          "feedback": "Erreur : la maxime reste plus que\njamais d'actualité. Avec la\nmontée en complexité des\nprogrammes modernes, l'enjeu de\nne pas s'égarer dans des\noptimisations inutiles est encore\nplus important.\n"
        },
        {
          "text": "Il ne faut jamais optimiser un\nprogramme\n",
          "correct": false,
          "feedback": "Erreur : Knuth ne dit pas\nd'abandonner toute optimisation,\nmais d'éviter celle qui n'est pas\nnécessaire ou qui se fait sans\nmesure. Optimiser les vrais\ngoulots d'étranglement est\nparfaitement légitime.\n"
        },
        {
          "text": "Il faut d'abord écrire un code\ncorrect et lisible, puis,\nseulement si nécessaire et\naprès mesure, optimiser les\nparties effectivement coûteuses\nidentifiées par le profilage\n",
          "correct": true,
          "feedback": "Bonne réponse : Knuth ajoutait\nensuite que « 97 % du temps, les\nmicro-optimisations sont à\noublier ». Sans mesure, on\noptimise souvent les mauvaises\nparties, on dégrade la lisibilité\net on introduit des bugs. Il faut\nmesurer avant d'agir : on parle\nde « pas d'optimisation aveugle ».\n"
        }
      ],
      "explanation": "Schéma à appliquer : (1) écrire un\nprogramme correct, lisible et\nmodulaire ; (2) le tester ; (3) si\nles performances sont insuffisantes,\nle profiler pour identifier les\nvéritables goulots ; (4) optimiser\nces goulots en mesurant l'impact ;\n(5) revérifier que tous les tests\npassent. Toute optimisation qui ne\nsuit pas ce schéma est suspecte."
    }
  ]
}