-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
276 lines (215 loc) · 9.14 KB
/
main.py
File metadata and controls
276 lines (215 loc) · 9.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# main.py – V5.0
"""
Point d'entrée principal pour DeFiPilot V5.0.
Rôle de ce fichier :
- Initialiser le logging de base.
- Analyser les arguments de la ligne de commande.
- Déléguer l'exécution au bon "runner" (GUI ou CLI) si disponible.
- Fournir des messages d'erreur clairs sans casser le projet.
Ce main est volontairement léger pour éviter les régressions :
la logique "métier" reste dans les autres modules (run_defipilot,
GUI, etc.).
"""
from __future__ import annotations
import logging
import os
import sys
from typing import List, Optional, Callable, Any
APP_VERSION = "V5.0"
# ---------------------------------------------------------------------------
# Utilitaires
# ---------------------------------------------------------------------------
def get_project_root() -> str:
"""Retourne le chemin racine supposé du projet (dossier de ce fichier)."""
return os.path.dirname(os.path.abspath(__file__))
def setup_logging() -> None:
"""Configure un logging simple vers la console et, si possible, vers un fichier."""
root = get_project_root()
logs_dir = os.path.join(root, "data", "logs")
os.makedirs(logs_dir, exist_ok=True)
log_file = os.path.join(logs_dir, "main_v5.log")
log_format = "[%(asctime)s] [%(levelname)s] %(name)s: %(message)s"
datefmt = "%Y-%m-%d %H:%M:%S"
# Handler fichier
file_handler = logging.FileHandler(log_file, encoding="utf-8")
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(logging.Formatter(log_format, datefmt=datefmt))
# Handler console
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter(log_format, datefmt=datefmt))
logging.basicConfig(
level=logging.INFO,
handlers=[file_handler, console_handler],
force=True,
)
logging.getLogger(__name__).info("DeFiPilot %s – logging initialisé.", APP_VERSION)
# ---------------------------------------------------------------------------
# Détection du mode
# ---------------------------------------------------------------------------
def detect_mode(argv: List[str]) -> str:
"""
Détermine le mode d'exécution à partir des arguments.
Modes possibles :
- 'gui' : lance l'interface graphique (si disponible)
- 'cli' : lance le moteur CLI principal (si disponible)
- 'simulate' : mode simulation (alias spécialisé de 'cli')
- 'help' : affiche l'aide
- défaut : 'gui' si disponible, sinon 'cli'
"""
if len(argv) < 2:
return "default"
arg = argv[1].lower().strip()
if arg in ("gui", "g"):
return "gui"
if arg in ("cli", "c"):
return "cli"
if arg in ("simulate", "sim"):
return "simulate"
if arg in ("help", "-h", "--help"):
return "help"
# Argument inconnu → on considère que c'est du CLI avec arguments
return "cli"
def print_help() -> None:
"""Affiche une aide rapide sur les modes possibles."""
msg = f"""
DeFiPilot {APP_VERSION} – Point d'entrée principal
Usage :
python main.py [mode] [options]
Modes :
gui, g Lance l'interface graphique (si disponible).
cli, c Lance le mode CLI principal (run_defipilot, etc.).
simulate, sim Lance une simulation si supportée par le runner CLI.
help, -h Affiche cette aide.
Sans argument :
- tente de lancer la GUI si elle est disponible ;
- sinon bascule automatiquement en mode CLI.
"""
print(msg.strip())
# ---------------------------------------------------------------------------
# Lancement GUI
# ---------------------------------------------------------------------------
def run_gui() -> int:
"""
Tente de lancer l'interface graphique principale.
- Essaie d'importer `gui.main_window`.
- Cherche une fonction `run()` ou `main()` dans ce module.
- Si rien n'est trouvé, loggue un message et retourne un code d'erreur doux.
"""
logger = logging.getLogger("DeFiPilot.GUI")
try:
import gui.main_window as gui_main # type: ignore[import]
except Exception as exc: # ImportError, ModuleNotFoundError, etc.
logger.error("Impossible d'importer gui.main_window : %s", exc)
logger.info("Astuce : vérifie que le dossier 'gui' est bien à la racine du projet.")
return 1
# On essaye d'appeler une fonction de lancement "standard"
launch_func: Optional[Callable[[], Any]] = None
if hasattr(gui_main, "run") and callable(getattr(gui_main, "run")):
launch_func = getattr(gui_main, "run")
logger.info("Lancement de l'interface graphique via gui.main_window.run()")
elif hasattr(gui_main, "main") and callable(getattr(gui_main, "main")):
launch_func = getattr(gui_main, "main")
logger.info("Lancement de l'interface graphique via gui.main_window.main()")
else:
logger.error(
"Le module gui.main_window ne définit ni 'run()' ni 'main()'. "
"Merci d'ajouter une de ces fonctions pour lancer la GUI."
)
return 1
try:
launch_func()
except Exception as exc: # pragma: no cover - protection runtime
logger.exception("Erreur lors de l'exécution de la GUI : %s", exc)
return 1
return 0
# ---------------------------------------------------------------------------
# Lancement CLI
# ---------------------------------------------------------------------------
def run_cli(mode: str, argv: List[str]) -> int:
"""
Tente de lancer le runner CLI principal.
Stratégie :
- Essaye d'importer `run_defipilot`.
- Si ce module contient une fonction `run()` ou `main(argv)`, on l'utilise.
- Sinon, loggue un message d'information et sort proprement.
Le paramètre `mode` permet éventuellement de transmettre une intention
spéciale ('simulate', etc.) au runner, si celui-ci la supporte.
"""
logger = logging.getLogger("DeFiPilot.CLI")
try:
import run_defipilot # type: ignore[import]
except Exception as exc: # ImportError, ModuleNotFoundError, etc.
logger.error("Impossible d'importer run_defipilot : %s", exc)
logger.info(
"Si tu utilises un autre script CLI (start_defipilot.py, simulateur_*.py, etc.), "
"tu peux l'appeler directement en attendant d'unifier les entrées."
)
return 1
# On essaie de détecter une fonction de lancement dans run_defipilot
func_with_argv: Optional[Callable[[List[str], str], Any]] = None
func_simple: Optional[Callable[[], Any]] = None
# Stratégie flexible : plusieurs signatures possibles
if hasattr(run_defipilot, "main"):
candidate = getattr(run_defipilot, "main")
if callable(candidate):
func_with_argv = candidate # on tentera main(argv, mode) puis main()
if hasattr(run_defipilot, "run") and callable(getattr(run_defipilot, "run")):
func_simple = getattr(run_defipilot, "run")
if func_with_argv is None and func_simple is None:
logger.error(
"Le module run_defipilot ne définit ni fonction main() exploitable "
"ni fonction run(). Merci d'ajouter un point d'entrée."
)
return 1
logger.info("Lancement du mode CLI (mode='%s') via run_defipilot", mode)
try:
# On essaye d'abord main(argv, mode), puis main(argv), puis main()
if func_with_argv is not None:
try:
# type: ignore[arg-type]
func_with_argv(argv, mode) # type: ignore[misc]
except TypeError:
try:
func_with_argv(argv) # type: ignore[misc]
except TypeError:
func_with_argv() # type: ignore[misc]
elif func_simple is not None:
func_simple()
except Exception as exc: # pragma: no cover - protection runtime
logger.exception("Erreur lors de l'exécution du runner CLI : %s", exc)
return 1
return 0
# ---------------------------------------------------------------------------
# main()
# ---------------------------------------------------------------------------
def main(argv: Optional[List[str]] = None) -> int:
"""
Point d'entrée principal.
- Initialise le logging.
- Détecte le mode d'exécution.
- Route vers GUI ou CLI.
"""
if argv is None:
argv = sys.argv
setup_logging()
logger = logging.getLogger("DeFiPilot.Main")
mode = detect_mode(argv)
logger.info("DeFiPilot %s – mode détecté : %s", APP_VERSION, mode)
if mode == "help":
print_help()
return 0
if mode == "gui":
return run_gui()
if mode in ("cli", "simulate"):
# Le mode "simulate" est géré à l'intérieur de run_cli via le paramètre `mode`.
return run_cli(mode, argv)
# Mode par défaut : on tente la GUI, puis on retombe sur le CLI si la GUI n'est pas dispo
logger.info("Mode par défaut : tentative de lancement GUI, puis fallback CLI.")
gui_status = run_gui()
if gui_status == 0:
return 0
logger.info("GUI indisponible ou en erreur, bascule vers le mode CLI.")
return run_cli("cli", argv)
if __name__ == "__main__":
sys.exit(main())