3131"""
3232
3333import os
34- from pathlib import Path
35- from typing import Any
34+ import fnmatch
35+ from pathlib import Path , PurePosixPath
36+ from typing import Any , Optional
3637import yaml
3738
3839
@@ -262,15 +263,35 @@ def get_environment_manager_options(config: dict[str, Any]) -> dict[str, Any]:
262263 return config .get ("environment_manager" , {})
263264
264265
266+ def _normalize_exclusion_pattern (pattern : str ) -> str :
267+ """
268+ Normalise un pattern d'exclusion pour un matching fiable cross-plateforme.
269+
270+ - Convertit les séparateurs Windows en "/"
271+ - Retire le préfixe "./"
272+ - Si le pattern se termine par "/", on l'étend à "/**"
273+ """
274+ p = str (pattern ).strip ()
275+ if not p :
276+ return ""
277+ p = p .replace ("\\ " , "/" )
278+ if p .startswith ("./" ):
279+ p = p [2 :]
280+ if p .endswith ("/" ):
281+ p = p .rstrip ("/" ) + "/**"
282+ return p
283+
284+
265285def should_exclude_file (
266- file_path : str , workspace_dir : str , exclusion_patterns : list [str ]
286+ file_path : str , workspace_dir : str , exclusion_patterns : Optional [ list [str ] ]
267287) -> bool :
268288 """
269289 Détermine si un fichier doit être exclu de la compilation.
270290
271291 Cette fonction compare le chemin du fichier avec les patterns
272- d'exclusion définis dans la configuration. Elle utilise la méthode
273- Path.match() qui supporte les patterns glob standards.
292+ d'exclusion définis dans la configuration. Elle normalise les
293+ séparateurs puis combine Path.match() et fnmatch pour supporter
294+ les patterns glob avec "**" de façon cross-plateforme.
274295
275296 Args:
276297 file_path: Chemin absolu du fichier à vérifier
@@ -281,23 +302,59 @@ def should_exclude_file(
281302 True si le fichier doit être exclu, False sinon
282303 """
283304 try :
305+ if not file_path or not workspace_dir :
306+ return False
307+
284308 file_path_obj = Path (file_path )
285309 workspace_path_obj = Path (workspace_dir )
286310
311+ # Résolution pour éviter les surprises (symlinks, chemins relatifs)
312+ try :
313+ file_abs = file_path_obj .resolve ()
314+ workspace_abs = workspace_path_obj .resolve ()
315+ except Exception :
316+ file_abs = file_path_obj
317+ workspace_abs = workspace_path_obj
318+
287319 # Calcul du chemin relatif par rapport au workspace
288320 try :
289- relative_path = file_path_obj .relative_to (workspace_path_obj )
321+ relative_path = file_abs .relative_to (workspace_abs )
290322 except ValueError :
291323 # Le fichier est hors du workspace
292324 return True
293325
326+ # Normalisation vers POSIX pour matcher les patterns avec "/"
327+ rel_posix = PurePosixPath (relative_path .as_posix ())
328+ abs_posix = PurePosixPath (file_abs .as_posix ())
329+ rel_str = rel_posix .as_posix ()
330+ abs_str = abs_posix .as_posix ()
331+ file_name = file_abs .name
332+
333+ patterns = exclusion_patterns or []
334+
294335 # Vérification des patterns d'exclusion
295- for pattern in exclusion_patterns :
296- # Comparaison avec le chemin relatif complet
297- if relative_path .match (pattern ):
298- return True
336+ for pattern in patterns :
337+ pat = _normalize_exclusion_pattern (pattern )
338+ if not pat :
339+ continue
340+
341+ # Patterns avec "**" : matcher via fnmatch pour supporter les répertoires
342+ if "**" in pat :
343+ if fnmatch .fnmatch (rel_str , pat ):
344+ return True
345+ if fnmatch .fnmatch (abs_str , pat ):
346+ return True
347+ else :
348+ # Comparaison avec le chemin relatif complet
349+ if rel_posix .match (pat ):
350+ return True
351+
352+ # Autoriser les patterns absolus si fournis par l'utilisateur
353+ if abs_posix .match (pat ):
354+ return True
355+
299356 # Comparaison avec juste le nom du fichier (pour patterns comme "*.pyc")
300- if file_path_obj .match (pattern ):
357+ if "/" not in pat and PurePosixPath ( file_name ) .match (pat ):
301358 return True
302359
303360 return False
0 commit comments