@@ -181,6 +181,110 @@ def _is_stdlib_module(module_name: str) -> bool:
181181 return False
182182
183183
184+ def _normalize_realpath (path : str | None ) -> str :
185+ if not path :
186+ return ""
187+ try :
188+ return os .path .realpath (path )
189+ except Exception :
190+ return path
191+
192+
193+ def _is_path_under (path : str , root : str ) -> bool :
194+ if not path or not root :
195+ return False
196+ try :
197+ return os .path .commonpath ([path , root ]) == root
198+ except Exception :
199+ return False
200+
201+
202+ def _collect_workspace_module_roots (
203+ filtered_files : list [str ], workspace_dir : str | None
204+ ) -> set [str ]:
205+ """
206+ Build a conservative set of internal module roots from workspace files.
207+ Handles common src-layout aliases (src/, lib/, python/).
208+ """
209+ roots : set [str ] = set ()
210+ ws = _normalize_realpath (workspace_dir )
211+ src_aliases = {"src" , "lib" , "python" }
212+ for f in filtered_files :
213+ try :
214+ abs_f = _normalize_realpath (f )
215+ base = os .path .splitext (os .path .basename (abs_f ))[0 ]
216+ if base and base != "__init__" :
217+ roots .add (base )
218+ if not ws or not _is_path_under (abs_f , ws ):
219+ continue
220+ rel = os .path .relpath (abs_f , ws )
221+ parts = rel .split (os .sep )
222+ if not parts :
223+ continue
224+ first = parts [0 ]
225+ if first and first not in {"" , "." , "__pycache__" }:
226+ roots .add (first )
227+ if first in src_aliases and len (parts ) > 1 :
228+ second = parts [1 ]
229+ if second and second not in {"" , "." , "__pycache__" }:
230+ roots .add (second )
231+ except Exception :
232+ pass
233+ return roots
234+
235+
236+ @functools .lru_cache (maxsize = 1024 )
237+ def _classify_module_origin (module_name : str , workspace_dir : str ) -> str :
238+ """
239+ Classify module origin:
240+ - stdlib: Python standard library / builtins
241+ - internal: module resolved under workspace_dir
242+ - third_party: resolved in site-packages/purelib/platlib/dist-packages
243+ - unknown: cannot resolve safely
244+ """
245+ if not module_name :
246+ return "unknown"
247+ if _is_stdlib_module (module_name ):
248+ return "stdlib"
249+ try :
250+ import importlib .util
251+ import sysconfig
252+
253+ ws = _normalize_realpath (workspace_dir )
254+ spec = importlib .util .find_spec (module_name )
255+ if spec is None :
256+ return "unknown"
257+ origin = getattr (spec , "origin" , None )
258+ if origin in ("built-in" , "frozen" ):
259+ return "stdlib"
260+
261+ candidates : list [str ] = []
262+ if origin :
263+ candidates .append (_normalize_realpath (origin ))
264+ for loc in spec .submodule_search_locations or []:
265+ candidates .append (_normalize_realpath (loc ))
266+
267+ stdlib_path = _normalize_realpath (sysconfig .get_path ("stdlib" ) or "" )
268+ purelib_path = _normalize_realpath (sysconfig .get_path ("purelib" ) or "" )
269+ platlib_path = _normalize_realpath (sysconfig .get_path ("platlib" ) or "" )
270+
271+ for c in candidates :
272+ if ws and _is_path_under (c , ws ):
273+ return "internal"
274+ if stdlib_path and _is_path_under (c , stdlib_path ):
275+ return "stdlib"
276+ if purelib_path and _is_path_under (c , purelib_path ):
277+ return "third_party"
278+ if platlib_path and _is_path_under (c , platlib_path ):
279+ return "third_party"
280+ if "site-packages" in c or "dist-packages" in c :
281+ return "third_party"
282+
283+ return "unknown"
284+ except Exception :
285+ return "unknown"
286+
287+
184288def _check_module_installed (module : str ) -> bool :
185289 """
186290 Vérifie si un module est installé via importlib.metadata (plus rPluginsde que subprocess pip show).
@@ -431,35 +535,39 @@ def _t(_key: str, fr: str, en: str) -> str:
431535 if analysis_progress :
432536 analysis_progress .set_message (self .tr ("Analyse terminée" , "Analysis completed" ))
433537 analysis_progress .set_progress (len (filtered_files ), len (filtered_files ))
434- # Exclure les modules standards Python (stdlib)
435- import sys
436- import sysconfig
437-
438- stdlib = set (sys .builtin_module_names )
439- # Ajoute les modules de la vraie stdlib (trouvés dans le dossier stdlib)
440- stdlib_paths = [sysconfig .get_path ("stdlib" )]
441- try :
442- import pkgutil
443-
444- for finder , name , ispkg in pkgutil .iter_modules (stdlib_paths ):
445- stdlib .add (name )
446- except Exception :
447- pass
448- # Exclure les modules internes du projet (présents dans le workspace)
449- internal_modules = set ()
450- for f in filtered_files :
451- base = os .path .splitext (os .path .basename (f ))[0 ]
452- internal_modules .add (base )
538+ # Classify imports as stdlib/internal/third-party/unknown.
539+ ws_root = _normalize_realpath (getattr (self , "workspace_dir" , None ) or "" )
540+ internal_modules = _collect_workspace_module_roots (filtered_files , ws_root )
453541 # Mise à jour du message de progression
454542 if analysis_progress :
455543 analysis_progress .set_message (
456544 self .tr ("Vérification des modules..." , "Checking modules..." )
457545 )
458546
459- # Liste des modules à vérifier (hors standard et hors modules internes)
460- suggestions = [
461- m for m in modules if not _is_stdlib_module (m ) and m not in internal_modules
462- ]
547+ suggestions = []
548+ category_stats = {"stdlib" : 0 , "internal" : 0 , "third_party" : 0 , "unknown" : 0 }
549+ for m in sorted (modules ):
550+ if m in internal_modules :
551+ category = "internal"
552+ else :
553+ category = _classify_module_origin (m , ws_root )
554+ if category == "unknown" and m in internal_modules :
555+ category = "internal"
556+ category_stats [category ] = category_stats .get (category , 0 ) + 1
557+ if category in ("third_party" , "unknown" ):
558+ suggestions .append (m )
559+
560+ try :
561+ _log_append (
562+ self ,
563+ "ℹ️ Imports classes: "
564+ f"stdlib={ category_stats .get ('stdlib' , 0 )} , "
565+ f"internal={ category_stats .get ('internal' , 0 )} , "
566+ f"third_party={ category_stats .get ('third_party' , 0 )} , "
567+ f"unknown={ category_stats .get ('unknown' , 0 )} " ,
568+ )
569+ except Exception :
570+ pass
463571 # Alerte spéciale pour tkinter (std lib optionnelle non installable via pip)
464572 try :
465573 import importlib .util as _il_util
0 commit comments