[FIX] todo: un PostgreSQL injoignable proposait sa trace d'appel comme base

« db --list » rendait un code de retour que personne ne lisait. Comme la sortie
et l'erreur passent par le même flux (stderr=STDOUT, execute.py), les lignes de
la trace d'appel devenaient les entrées du menu :

  [1] Traceback (most recent call last):
  [2]   File "…/odoo-bin", line 8, in <module>
  [0] Retour

Choisir [1] renvoyait « Traceback (most recent call last): » comme nom de base à
l'appelant, qui le passait à sa commande — sauvegarde, restauration, shell. Le
code de retour était pourtant là, 120, dans la variable « status », écrasée deux
lignes plus bas par la réponse de click.prompt.

Le contrôle se fait donc AVANT de construire le menu : code non nul, on affiche
les dernières lignes de l'erreur réelle et on rend False. Une liste vide dit
aussi ce qu'elle est, au lieu d'un écran ne portant que « Retour ».

Au passage, la branche de saisie invalide appelait t("cmd_not_found") — une clé
absente de TRANSLATIONS, donc affichée telle quelle, en anglais technique. Les
autres menus disent t("Command not found !") ; celui-ci le dit maintenant aussi.

« quiet=True » s'ajoute parce que la sortie brute n'a plus de raison d'être vue :
le menu affiche la liste, et en cas d'échec c'est l'erreur qui est montrée.

Vérifié sur les quatre chemins, PGPORT=1 pour simuler la panne : injoignable
rend False sans afficher un seul menu ; [0] rend False ; [1] rend « test » ; une
saisie invalide affiche un message traduit dans les deux langues.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mathieu Benoit 2026-08-03 08:03:42 +00:00
parent bc41b35769
commit 55f6127a72
2 changed files with 43 additions and 8 deletions

View file

@ -31,28 +31,51 @@ class DatabaseManager:
self._dir_path = path self._dir_path = path
def select_database(self) -> str | bool: def select_database(self) -> str | bool:
"""Faire choisir une base parmi celles que PostgreSQL expose.
Le code de retour de « db --list » est vérifié AVANT de construire le
menu. Sans cette vérification, un PostgreSQL injoignable ne se distingue
pas d'une base absente : la sortie et l'erreur sont fusionnées dans le
même flux (`stderr=STDOUT`, execute.py), donc les lignes de la trace
d'appel devenaient les entrées du menu. « Traceback (most recent call
last): » s'affichait comme la base [1], et la choisir renvoyait cette
ligne comme nom de base à l'appelant, qui la passait à sa commande.
"""
cmd_server = "./odoo_bin.sh db --list" cmd_server = "./odoo_bin.sh db --list"
status, databases = self._execute.exec_command_live( status, output = self._execute.exec_command_live(
cmd_server, cmd_server,
return_status_and_output=True, return_status_and_output=True,
quiet=True,
source_erplibre=False, source_erplibre=False,
single_source_erplibre=True, single_source_erplibre=True,
) )
choices = [{"prompt_description": a.strip()} for a in databases] if status:
print(f"{t('Cannot list the databases (exit code): ')}{status}")
print(f" {t('Is PostgreSQL running?')}")
for line in output[-5:]:
print(f" {line}")
return False
databases = [a.strip() for a in output if a.strip()]
if not databases:
print(f" {t('No database on this PostgreSQL server.')}")
return False
choices = [{"prompt_description": a} for a in databases]
help_info = self._fill_help_info(choices) help_info = self._fill_help_info(choices)
valid_choices = [str(a) for a in range(len(choices) + 1) if a] valid_choices = [str(a + 1) for a in range(len(databases))]
while True: while True:
status = click.prompt(help_info) answer = click.prompt(help_info)
print() print()
if status == "0": if answer == "0":
return False return False
elif status in valid_choices: elif answer in valid_choices:
database_name = databases[int(status) - 1].strip() database_name = databases[int(answer) - 1]
print(database_name) print(database_name)
return database_name return database_name
else: else:
print(t("cmd_not_found")) print(t("Command not found !"))
def _confirm_drop(self, message: str) -> bool: def _confirm_drop(self, message: str) -> bool:
"""Ask for an explicit 'oui'/'yes' confirmation, default is no.""" """Ask for an explicit 'oui'/'yes' confirmation, default is no."""

View file

@ -478,6 +478,18 @@ TRANSLATIONS = {
"fr": "Aucune base de données sélectionnée.", "fr": "Aucune base de données sélectionnée.",
"en": "No database selected.", "en": "No database selected.",
}, },
"Cannot list the databases (exit code): ": {
"fr": "Impossible de lister les bases (code de retour) : ",
"en": "Cannot list the databases (exit code): ",
},
"Is PostgreSQL running?": {
"fr": "PostgreSQL est-il démarré ?",
"en": "Is PostgreSQL running?",
},
"No database on this PostgreSQL server.": {
"fr": "Aucune base sur ce serveur PostgreSQL.",
"en": "No database on this PostgreSQL server.",
},
"Manage execution processes!": { "Manage execution processes!": {
"fr": "Manipuler les processus d'exécution!", "fr": "Manipuler les processus d'exécution!",
"en": "Manage execution processes!", "en": "Manage execution processes!",