vous allez écrire une appli CLI pour gérer des tâches (todo). les tâches seront stockées dans un fichier json local.
objectifs
- concevoir une interface CLI (
add/list/done/rm/clear) - sérialiser et charger les tâches (json)
- gérer des erreurs et des codes de sortie
explication détaillée
format
fichier todos.json :
[
{"id": 1, "title": "acheter du lait", "done": false},
{"id": 2, "title": "lire un livre", "done": true}
]
commandes
add "titre"→ ajouterlist→ afficher toutes les tâchesdone ID→ marquer terminéerm ID→ supprimerclear→ supprimer toutes
exemples exécutables
# todo.py
import json, sys
from pathlib import Path
import argparse
DB = Path("todos.json")
def load():
if not DB.exists():
return []
return json.loads(DB.read_text(encoding="utf-8"))
def save(items):
DB.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8")
def next_id(items):
return (max([it["id"] for it in items]) + 1) if items else 1
def cmd_add(title: str) -> None:
items = load()
items.append({"id": next_id(items), "title": title.strip(), "done": False})
save(items)
print("ok")
def cmd_list() -> None:
items = load()
for it in items:
flag = "✔" if it["done"] else "·"
print(f"{it['id']:>3} {flag} {it['title']}")
def cmd_done(i: int) -> int:
items = load()
for it in items:
if it["id"] == i:
it["done"] = True
save(items)
print("done")
return 0
print("id introuvable", file=sys.stderr)
return 1
def cmd_rm(i: int) -> int:
items = load()
new = [it for it in items if it["id"] != i]
if len(new) == len(items):
print("id introuvable", file=sys.stderr)
return 1
save(new)
print("removed")
return 0
def cmd_clear() -> None:
save([])
print("cleared")
def main(argv=None):
p = argparse.ArgumentParser(prog="todo", description="todo cli")
sub = p.add_subparsers(dest="cmd", required=True)
p_add = sub.add_parser("add")
p_add.add_argument("title")
sub.add_parser("list")
p_done = sub.add_parser("done")
p_done.add_argument("id", type=int)
p_rm = sub.add_parser("rm")
p_rm.add_argument("id", type=int)
sub.add_parser("clear")
args = p.parse_args(argv)
if args.cmd == "add":
cmd_add(args.title)
elif args.cmd == "list":
cmd_list()
elif args.cmd == "done":
sys.exit(cmd_done(args.id))
elif args.cmd == "rm":
sys.exit(cmd_rm(args.id))
elif args.cmd == "clear":
cmd_clear()
if __name__ == "__main__":
main()
bonnes pratiques
- valider les entrées (
idnumérique,titlenon vide) - messages clairs + code de sortie ≠ 0 en cas d’erreur
- tests automatisés sur le fichier json (utiliser
tmp_path)
pièges courants
- écraser le fichier en cas d’erreur → écrire dans un fichier temporaire puis remplacer (bonus)
- oublier
ensure_ascii=Falsepour les accents
exercices
- ajouter une date de création ISO (
created_at). - ajouter un filtre
list --done/--todo. - écrire 3 tests
pytest(add/list/done) avectmp_path.