102102# Elles sont essentielles pour le bon fonctionnement de Python et du système.
103103
104104_DEF_ENV_KEYS = (
105- "PATH" , # Chemins des exécutables
106- "LANG" , # Paramètres linguistiques
107- "LC_ALL" , # Paramètres régionaux complets
108- "LC_CTYPE" , # Type de caractères
109- "TMP" , # Répertoire temporaire (Windows)
110- "TEMP" , # Répertoire temporaire (Windows, alternatif)
105+ "PATH" , # Chemins des exécutables
106+ "LANG" , # Paramètres linguistiques
107+ "LC_ALL" , # Paramètres régionaux complets
108+ "LC_CTYPE" , # Type de caractères
109+ "TMP" , # Répertoire temporaire (Windows)
110+ "TEMP" , # Répertoire temporaire (Windows, alternatif)
111111)
112112
113113
114- def validate_args (
115- args : Sequence [Any ],
116- * ,
117- max_len : int = 4096
118- ) -> list [str ]:
114+ def validate_args (args : Sequence [Any ], * , max_len : int = 4096 ) -> list [str ]:
119115 """
120116 Valide et normalise une séquence d'arguments CLI.
121-
117+
122118 Cette fonction effectue plusieurs vérifications de sécurité essentielles:
123-
119+
124120 1. **Rejet des valeurs None** : Les arguments None sont invalides et
125121 indiquent généralement une erreur de programmation.
126-
122+
127123 2. **Rejet des caractères de contrôle** : Les caractères comme newline
128124 (\\ n), carriage return (\\ r) et null (\\ x00) peuvent être utilisés pour
129125 injecter des commandes supplémentaires.
130-
126+
131127 3. **Limite de longueur** : Empêche les attaques par débordement de
132128 tampon et les arguments excessivement longs.
133-
129+
134130 4. **Conversion en chaînes** : Tous les types sont convertis en strings
135131 pour assurer la cohérence du typage.
136-
132+
137133 Args:
138134 args: Séquence d'arguments à valider. Peut contenir des objets de
139135 types variés (Path, int, str, etc.) qui seront convertis.
140136 max_len: Longueur maximale autorisée pour chaque argument.
141137 Par défaut 4096 caractères.
142-
138+
143139 Returns:
144140 Liste de chaînes de caractères validées et normalisées.
145-
141+
146142 Raises:
147143 ValueError: Si un argument est None, contient des caractères de
148144 contrôle invalides, ou dépasse la longueur maximale.
149-
145+
150146 Exemples:
151147 >>> validate_args(["--input", "file.py", 123])
152148 ['--input', 'file.py', '123']
153-
149+
154150 >>> validate_args(["--name", "test"])
155151 ['--name', 'test']
156-
152+
157153 >>> validate_args([None]) # ValueError
158154 ValueError: Argument is None
159-
155+
160156 >>> validate_args(["--data", "line\\ nbreak"]) # ValueError
161157 ValueError: Invalid control character in argument...
162-
158+
163159 Notes:
164160 - Cette validation est exécutée AVANT toute exécution de processus.
165161 - Elle protège contre les injections de commandes shell.
@@ -196,22 +192,22 @@ def build_env(
196192) -> dict [str , str ]:
197193 """
198194 Construit un dictionnaire d'environnement sécurisé pour subprocess/QProcess.
199-
195+
200196 Cette fonction crée un environnement minimal en suivant ce processus:
201-
197+
202198 1. **Initialisation** : Part d'un mapping vide ou du 'base' fourni.
203-
199+
204200 2. **Filtrage** : Ne conserve que les variables whitelisted. Si aucune
205201 whitelist n'est fournie, utilise la liste par défaut (_DEF_ENV_KEYS).
206-
202+
207203 3. **Injection** : Ajoute ou écrase les variables avec 'extra'.
208-
204+
209205 4. **PATH** : Remplace éventuellement le PATH par 'minimal_path'.
210-
206+
211207 L'objectif est de créer un environnement isolé et prévisible pour les
212208 processus de compilation, en évitant les variables potentiellement
213209 dangereuses ou incohérentes.
214-
210+
215211 Args:
216212 base: Environnement de base à utiliser. Si None, commence avec
217213 un dictionnaire vide.
@@ -220,30 +216,30 @@ def build_env(
220216 extra: Variables supplémentaires à ajouter ou écraser dans l'env.
221217 minimal_path: Valeur fixe pour la variable PATH. Si fourni, remplace
222218 complètement le PATH existant.
223-
219+
224220 Returns:
225221 Dictionnaire contenant l'environnement construit, prêt à être passé
226222 à subprocess ou QProcess.
227-
223+
228224 Exemples:
229225 >>> # Environnement minimal avec variables par défaut
230226 >>> env = build_env()
231227 >>> print(env.keys())
232228 dict_keys(['PATH', 'LANG', 'LC_ALL', 'LC_CTYPE', 'TMP', 'TEMP'])
233-
229+
234230 >>> # À partir de l'environnement courant, en gardant seulement PATH et HOME
235231 >>> env = build_env(
236232 ... base=os.environ,
237233 ... whitelist=["PATH", "HOME"],
238234 ... extra={"MY_VAR": "value"}
239235 ... )
240-
236+
241237 >>> # PATH personnalisé (pour isoler l'environnement de compilation)
242238 >>> env = build_env(
243239 ... base=os.environ,
244240 ... minimal_path="/custom/bin:/usr/local/bin"
245241 ... )
246-
242+
247243 Notes:
248244 - Les variables non-string sont ignorées.
249245 - L'ordre de priorité: extra > minimal_path > whitelist > base
@@ -253,22 +249,22 @@ def build_env(
253249 env : dict [str , str ] = {}
254250 src = dict (base or {})
255251 allow = set (whitelist or _DEF_ENV_KEYS )
256-
252+
257253 # Filtrage des variables selon la whitelist
258254 for k , v in src .items ():
259255 if k in allow and isinstance (v , str ):
260256 env [k ] = v
261-
257+
262258 # Remplacement du PATH si demandé
263259 if minimal_path is not None :
264260 env ["PATH" ] = minimal_path
265-
261+
266262 # Ajout des variables supplémentaires
267263 if extra :
268264 for k , v in extra .items ():
269265 if isinstance (k , str ) and isinstance (v , str ):
270266 env [k ] = v
271-
267+
272268 return env
273269
274270
@@ -281,35 +277,34 @@ def build_env(
281277
282278
283279def normalized_program_and_args (
284- program : Pathish ,
285- args : Sequence [Any ]
280+ program : Pathish , args : Sequence [Any ]
286281) -> tuple [str , list [str ]]:
287282 """
288283 Normalise le programme et ses arguments en types sûrs.
289-
284+
290285 Cette fonction de commodité combine deux opérations:
291286 1. Conversion du chemin du programme en chaîne de caractères
292287 2. Validation de tous les arguments avec validate_args()
293-
288+
294289 Args:
295290 program: Chemin vers l'exécutable (peut être str ou Path).
296291 args: Séquence d'arguments pour le programme.
297-
292+
298293 Returns:
299294 Tuple (programme_str, args_validés) où:
300295 - programme_str: Chemin du programme en string
301296 - args_validés: Liste d'arguments validés et normalisés
302-
297+
303298 Raises:
304299 ValueError: Si un argument est invalide (voir validate_args).
305-
300+
306301 Exemples:
307302 >>> normalized_program_and_args(Path("/usr/bin/python"), ["-c", "print(1)"])
308303 ('/usr/bin/python', ['-c', 'print(1)'])
309-
304+
310305 >>> normalized_program_and_args("python", ["script.py"])
311306 ('python', ['script.py'])
312-
307+
313308 Notes:
314309 - Cette fonction est généralement appelée avant run_process().
315310 - Elle garantit que tous les types sont normalisés avant exécution.
@@ -339,25 +334,25 @@ def run_process(
339334) -> tuple [int , str , str ]:
340335 """
341336 Exécute un processus en utilisant QProcess (Qt) ou subprocess.
342-
337+
343338 Cette fonction est le point d'entrée principal pour l'exécution de
344339 processus dans PyCompiler ARK. Elle offre plusieurs avantages:
345-
340+
346341 - **QProcess (Préféré)** : Meilleure intégration avec l'event loop Qt,
347342 support natif des signaux/slots, et gestion plus propre des processus.
348-
343+
349344 - **Subprocess (Fallback)** : Si QProcess échoue ou n'est pas disponible,
350345 utilise subprocess.run() comme solution de repli.
351-
346+
352347 - **Gestion du Répertoire** : Utilise automatiquement gui.workspace_dir
353348 comme répertoire de travail si aucun n'est spécifié.
354-
349+
355350 - **Callbacks Optionnels** : Appelle on_stdout/on_stderr après la
356351 complétion du processus avec le contenu complet des buffers.
357-
352+
358353 - **Timeout Intelligent** : Gère proprement les timeouts avec tentative
359354 d'arrêt propre (terminate) avant forçage (kill).
360-
355+
361356 Args:
362357 gui: Instance GUI (MainWindow) contenant workspace_dir et log.
363358 Utilisé pour déterminer le répertoire de travail par défaut.
@@ -371,16 +366,16 @@ def run_process(
371366 Signature: callback(stdout: str) -> None
372367 on_stderr: Callback(optionnel) appelé avec les données stderr.
373368 Signature: callback(stderr: str) -> None
374-
369+
375370 Returns:
376371 Tuple (exit_code, stdout, stderr):
377372 - exit_code: Code de retour du processus (0 = succès)
378373 - stdout: Sortie standard capturée (string)
379374 - stderr: Sortie d'erreur capturée (string)
380-
375+
381376 Raises:
382377 - Ne lève jamais d'exception ; retourne (1, "", str(e)) en cas d'erreur.
383-
378+
384379 Exemples:
385380 >>> # Exécution simple
386381 >>> code, out, err = run_process(
@@ -389,21 +384,21 @@ def run_process(
389384 ... )
390385 >>> print(out)
391386 hello
392-
387+
393388 >>> # Avec répertoire personnalisé
394389 >>> code, out, err = run_process(
395390 ... gui, "python", ["script.py"],
396391 ... cwd="/path/to/workdir"
397392 ... )
398-
393+
399394 >>> # Avec callbacks
400395 >>> def log_output(text):
401396 ... print(f"OUTPUT: {text}")
402397 >>> code, out, err = run_process(
403398 ... gui, "python", ["script.py"],
404399 ... on_stdout=log_output
405400 ... )
406-
401+
407402 Flux d'Exécution:
408403 ┌─────────────────────────────────────────────────────────────┐
409404 │ 1. Normaliser programme + arguments │
@@ -418,7 +413,7 @@ def run_process(
418413 │ 5. Exécuter les callbacks (si fournis) │
419414 │ 6. Retourner (code, stdout, stderr) │
420415 └─────────────────────────────────────────────────────────────┘
421-
416+
422417 Notes:
423418 - Les callbacks sont appelés APRÈS la complétion du processus,
424419 pas en streaming temps réel.
@@ -561,4 +556,3 @@ def run_process(
561556# =============================================================================
562557# FIN DU MODULE command_helpers.py
563558# =============================================================================
564-
0 commit comments