diff --git a/backend/app/dependencies.py b/backend/app/dependencies.py index 30463b9..c3c4b06 100644 --- a/backend/app/dependencies.py +++ b/backend/app/dependencies.py @@ -806,6 +806,10 @@ def resolve_dependencies( if dep_artifact_id == artifact_id: continue + # Skip if this artifact is already being visited (would cause cycle) + if dep_artifact_id in visiting: + continue + _resolve_recursive( dep_artifact_id, dep.dependency_project, @@ -1142,14 +1146,19 @@ async def resolve_dependencies_with_fetch( ).all() for dep in deps: - # Skip self-dependencies + # Skip self-dependencies (common with PyPI extras like pytest[testing] -> pytest) dep_proj_normalized = dep.dependency_project.lower() dep_pkg_normalized = _normalize_pypi_package_name(dep.dependency_package) curr_proj_normalized = proj_name.lower() curr_pkg_normalized = _normalize_pypi_package_name(pkg_name) if dep_proj_normalized == curr_proj_normalized and dep_pkg_normalized == curr_pkg_normalized: + logger.debug( + f"Skipping self-dependency: {pkg_key} -> {dep.dependency_project}/{dep.dependency_package}" + ) continue + # Also check if this dependency would resolve to the current artifact + # (handles cases where package names differ but resolve to same artifact) resolved_dep = _resolve_dependency_to_artifact( db, dep.dependency_project, @@ -1185,7 +1194,20 @@ async def resolve_dependencies_with_fetch( dep_artifact_id, dep_version, dep_size = resolved_dep + # Skip if resolved to same artifact (self-dependency at artifact level) if dep_artifact_id == artifact_id: + logger.debug( + f"Skipping self-dependency (same artifact): {pkg_key} -> " + f"{dep.dependency_project}/{dep.dependency_package} (artifact {dep_artifact_id[:12]})" + ) + continue + + # Skip if this artifact is already being visited (would cause cycle) + if dep_artifact_id in visiting: + logger.debug( + f"Skipping dependency already in resolution stack: {pkg_key} -> " + f"{dep.dependency_project}/{dep.dependency_package} (artifact {dep_artifact_id[:12]})" + ) continue await _resolve_recursive_async(