diff --git a/FQDN_NORMALIZATION_FIX.py b/FQDN_NORMALIZATION_FIX.py index 7c5fc3f4..6ef34519 100644 --- a/FQDN_NORMALIZATION_FIX.py +++ b/FQDN_NORMALIZATION_FIX.py @@ -13,6 +13,11 @@ 4. Partial path matching for complex Java packages Replace the normalization loop in cluster_modules.py:212-233 with this code. + +NOTE: This is a scratch/reference module, not imported anywhere in the +codebase. It documents a proposed patch for cluster_modules.py and should be +merged into that file (or moved to docs/ or a PR description) rather than +kept as standalone code at the repository root. """ from typing import Dict, List @@ -135,6 +140,7 @@ def normalize_component_ids_enhanced( # This handles cases where LLM includes partial path # Example: "deps.openframe-oss-lib.src.main.java.Class" # should match "openframe-oss-lib.different.path.java.Class" + suffix_matches: List[str] = [] if '.' in comp_id: # Try matching last 2-4 segments segments = comp_id.split('.') diff --git a/codewiki/src/be/cluster_modules.py b/codewiki/src/be/cluster_modules.py index 6866d043..7893cf71 100644 --- a/codewiki/src/be/cluster_modules.py +++ b/codewiki/src/be/cluster_modules.py @@ -33,8 +33,18 @@ def extract_module_hint(fqdn: str) -> str: "openframe-oss-lib.openframe-api-service-core..." → "api-service" "main-repo.src/services/auth.py::AuthService" → "auth" """ + if '::' not in fqdn: + logger.warning( + f"FQDN '{fqdn}' does not conform to the required 'module.path::ClassName' " + f"format (missing '::' separator); rejecting dot-only FQDN for module hint extraction" + ) + return "unknown" + + # Only operate on the module/path portion before the '::' separator + module_part = fqdn.split('::')[0] + # Strategy 1: Look for service-like patterns (openframe-api-service → api-service) - parts = fqdn.split('.') + parts = module_part.split('.') for part in parts: if '-service' in part or '-api' in part: # Extract meaningful part (e.g., "openframe-api-service" → "api-service") @@ -43,13 +53,10 @@ def extract_module_hint(fqdn: str) -> str: return '-'.join(segments[-2:]) # Strategy 2: Extract from file path (src/services/auth.py → auth) - if '::' in fqdn: - file_path = fqdn.split('::')[0] - # Get last meaningful directory or file name - path_parts = file_path.replace('\\', '/').split('/') - for part in reversed(path_parts): - if part and part not in ['src', 'main', 'java', 'com']: - return part.replace('.py', '').replace('.java', '').replace('.ts', '') + path_parts = module_part.replace('\\', '/').split('/') + for part in reversed(path_parts): + if part and part not in ['src', 'main', 'java', 'com']: + return part.replace('.py', '').replace('.java', '').replace('.ts', '') # Fallback: Use first segment return parts[0] if parts else "unknown" @@ -63,26 +70,34 @@ def extract_package_hint(fqdn: str) -> str: "...src.main.java.com.openframe.api.controller.Class" → "controller" "main-repo.src/models/device.py::DeviceModel" → "models" """ + if '::' not in fqdn: + logger.warning( + f"FQDN '{fqdn}' does not conform to the required 'module.path::ClassName' " + f"format (missing '::' separator); rejecting dot-only FQDN for package hint extraction" + ) + return "core" + + # Only operate on the module/path portion before the '::' separator + module_part = fqdn.split('::')[0] + # Strategy 1: Look for common package patterns common_packages = ['controller', 'service', 'repository', 'model', 'dto', 'config', 'util', 'helper', 'handler', 'processor'] - fqdn_lower = fqdn.lower() + module_part_lower = module_part.lower() for pkg in common_packages: - if pkg in fqdn_lower: + if pkg in module_part_lower: return pkg # Strategy 2: Extract from file path structure - if '::' in fqdn: - file_path = fqdn.split('::')[0] - path_parts = file_path.replace('\\', '/').split('/') - # Look for meaningful directory names - for part in reversed(path_parts[:-1]): # Skip filename - if part and part not in ['src', 'main', 'java', 'com', 'org']: - return part - - # Fallback: Extract from path - parts = fqdn.split('.') + path_parts = module_part.replace('\\', '/').split('/') + # Look for meaningful directory names + for part in reversed(path_parts[:-1]): # Skip filename + if part and part not in ['src', 'main', 'java', 'com', 'org']: + return part + + # Fallback: Extract from dotted module path + parts = module_part.split('.') if len(parts) >= 2: return parts[-2] diff --git a/codewiki/src/be/dependency_analyzer/analyzers/python.py b/codewiki/src/be/dependency_analyzer/analyzers/python.py index 8346e015..e136587d 100644 --- a/codewiki/src/be/dependency_analyzer/analyzers/python.py +++ b/codewiki/src/be/dependency_analyzer/analyzers/python.py @@ -1,3 +1,12 @@ +"""Python AST-based dependency analyzer. + +This module implements an AST-based analyzer for Python source files, extracting +classes, functions, and their call relationships for use in the dependency +analysis pipeline. Component IDs are generated as module::ClassName style +fully-qualified names to remain consistent with the dependency graph and +clustering system's expected FQDN format. +""" + import ast import logging import warnings