Skip to content

Commit ff8c8fe

Browse files
committed
deps-analyser: classification robuste des imports internes/externes
- Ajoute une classification d'origine des modules (stdlib/internal/third_party/unknown) via importlib.find_spec et chemins réels. - Distingue mieux les modules internes du workspace, y compris les layouts src/lib/python. - Corrige le filtre des fichiers analysés (venv, caches, build, dist, egg-info, etc.) qui était partiellement défaillant. - Ajoute un log de synthèse des classes d'imports pour faciliter le diagnostic.
1 parent f0463ad commit ff8c8fe

1 file changed

Lines changed: 87 additions & 2 deletions

File tree

Core/deps_analyser/analyser.py

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515

1616
import functools
1717
import configparser
18+
import importlib.util
1819
import json
1920
import os
2021
import platform
2122
import re
2223
import subprocess
24+
import sys
25+
import sysconfig
2326
import yaml
2427
from importlib.metadata import distribution, PackageNotFoundError
2528

@@ -142,6 +145,89 @@ def _load_excluded_stdlib() -> set[str]:
142145
EXCLUDED_STDLIB = _load_excluded_stdlib()
143146

144147

148+
def _normalize_realpath(path: str) -> str:
149+
try:
150+
return os.path.realpath(os.path.abspath(path))
151+
except Exception:
152+
return path
153+
154+
155+
def _is_path_under(path: str, root: str) -> bool:
156+
if not path or not root:
157+
return False
158+
try:
159+
return os.path.commonpath([_normalize_realpath(path), _normalize_realpath(root)]) == _normalize_realpath(root)
160+
except Exception:
161+
return False
162+
163+
164+
def _collect_workspace_module_roots(filtered_files: list[str], workspace_dir: str) -> set[str]:
165+
roots: set[str] = set()
166+
ws_root = _normalize_realpath(workspace_dir)
167+
if not ws_root:
168+
return roots
169+
for file_path in filtered_files:
170+
norm = _normalize_realpath(file_path)
171+
if not _is_path_under(norm, ws_root):
172+
continue
173+
rel_path = os.path.relpath(norm, ws_root)
174+
rel_no_ext = os.path.splitext(rel_path)[0]
175+
parts = [p for p in rel_no_ext.split(os.sep) if p]
176+
if not parts:
177+
continue
178+
# src/lib/python layouts: prefer first meaningful package component.
179+
if parts[0] in {"src", "lib", "python"} and len(parts) > 1:
180+
roots.add(parts[1])
181+
roots.add(parts[0])
182+
if len(parts) > 1 and parts[-1] == "__init__":
183+
roots.add(parts[-2])
184+
return roots
185+
186+
187+
@functools.lru_cache(maxsize=1024)
188+
def _classify_module_origin(module_name: str, workspace_dir: str) -> str:
189+
if not module_name:
190+
return "unknown"
191+
if module_name in EXCLUDED_STDLIB:
192+
return "stdlib"
193+
if module_name in sys.builtin_module_names:
194+
return "stdlib"
195+
try:
196+
spec = importlib.util.find_spec(module_name)
197+
except Exception:
198+
return "unknown"
199+
if spec is None:
200+
return "unknown"
201+
if getattr(spec, "origin", None) in ("built-in", "frozen"):
202+
return "stdlib"
203+
204+
candidates = []
205+
if getattr(spec, "origin", None):
206+
candidates.append(_normalize_realpath(spec.origin))
207+
for loc in getattr(spec, "submodule_search_locations", []) or []:
208+
candidates.append(_normalize_realpath(loc))
209+
210+
ws_root = _normalize_realpath(workspace_dir)
211+
for c in candidates:
212+
if _is_path_under(c, ws_root):
213+
return "internal"
214+
215+
stdlib_path = _normalize_realpath(sysconfig.get_path("stdlib") or "")
216+
for c in candidates:
217+
if _is_path_under(c, stdlib_path):
218+
return "stdlib"
219+
220+
purelib = _normalize_realpath(sysconfig.get_path("purelib") or "")
221+
platlib = _normalize_realpath(sysconfig.get_path("platlib") or "")
222+
for c in candidates:
223+
if _is_path_under(c, purelib) or _is_path_under(c, platlib):
224+
return "third_party"
225+
if "site-packages" in c or "dist-packages" in c:
226+
return "third_party"
227+
228+
return "unknown"
229+
230+
145231
@functools.lru_cache(maxsize=256)
146232
def _is_stdlib_module(module_name: str) -> bool:
147233
"""
@@ -627,8 +713,7 @@ def _t(_key: str, fr: str, en: str) -> str:
627713
if analysis_progress:
628714
analysis_progress.set_message(self.tr("Analyse terminée", "Analysis completed"))
629715
analysis_progress.set_progress(len(filtered_files), len(filtered_files))
630-
# Classify imports as stdlib/internal/third-party/unknown.
631-
ws_root = _normalize_realpath(getattr(self, "workspace_dir", None) or "")
716+
ws_root = _normalize_realpath(self.workspace_dir or "")
632717
internal_modules = _collect_workspace_module_roots(filtered_files, ws_root)
633718
# Mise à jour du message de progression
634719
if analysis_progress:

0 commit comments

Comments
 (0)