feat: add advanced dependency resolution and package download tools
- Add DependencyParser for parsing and categorizing package dependencies - Add DependencyResolver for recursive dependency tree analysis - Add PackageDownloader for downloading packages with dependencies - Add resolve_dependencies MCP tool for comprehensive dependency analysis - Add download_package MCP tool for package collection - Support Python version filtering and extra dependencies - Include comprehensive test coverage for new functionality - Add demonstration script for new features - Update README with new capabilities and usage examples Signed-off-by: Hal <hal.long@outlook.com>
This commit is contained in:
parent
04c98b26db
commit
6b14ff6da5
9 changed files with 1540 additions and 1 deletions
157
examples/dependency_analysis_demo.py
Normal file
157
examples/dependency_analysis_demo.py
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Demonstration of PyPI Query MCP Server dependency analysis and download features.
|
||||
|
||||
This script shows how to use the new dependency resolution and package download
|
||||
capabilities for analyzing Python packages like PySide2.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from pypi_query_mcp.tools.dependency_resolver import resolve_package_dependencies
|
||||
from pypi_query_mcp.tools.package_downloader import download_package_with_dependencies
|
||||
|
||||
|
||||
async def analyze_pyside2_dependencies():
|
||||
"""Analyze PySide2 dependencies for Python 3.10."""
|
||||
print("🔍 Analyzing PySide2 dependencies for Python 3.10...")
|
||||
|
||||
try:
|
||||
result = await resolve_package_dependencies(
|
||||
package_name="PySide2",
|
||||
python_version="3.10",
|
||||
include_extras=[],
|
||||
include_dev=False,
|
||||
max_depth=3
|
||||
)
|
||||
|
||||
print(f"✅ Successfully resolved dependencies for {result['package_name']}")
|
||||
print(f"📊 Summary:")
|
||||
summary = result['summary']
|
||||
print(f" - Total packages: {summary['total_packages']}")
|
||||
print(f" - Runtime dependencies: {summary['total_runtime_dependencies']}")
|
||||
print(f" - Max depth: {summary['max_depth']}")
|
||||
|
||||
print(f"\n📦 Package list:")
|
||||
for i, pkg in enumerate(summary['package_list'][:10], 1): # Show first 10
|
||||
print(f" {i}. {pkg}")
|
||||
|
||||
if len(summary['package_list']) > 10:
|
||||
print(f" ... and {len(summary['package_list']) - 10} more packages")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error analyzing dependencies: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def download_pyside2_packages():
|
||||
"""Download PySide2 and its dependencies."""
|
||||
print("\n📥 Downloading PySide2 and dependencies...")
|
||||
|
||||
download_dir = Path("./pyside2_downloads")
|
||||
|
||||
try:
|
||||
result = await download_package_with_dependencies(
|
||||
package_name="PySide2",
|
||||
download_dir=str(download_dir),
|
||||
python_version="3.10",
|
||||
include_extras=[],
|
||||
include_dev=False,
|
||||
prefer_wheel=True,
|
||||
verify_checksums=True,
|
||||
max_depth=2 # Limit depth for demo
|
||||
)
|
||||
|
||||
print(f"✅ Download completed!")
|
||||
print(f"📊 Download Summary:")
|
||||
summary = result['summary']
|
||||
print(f" - Total packages: {summary['total_packages']}")
|
||||
print(f" - Successful downloads: {summary['successful_downloads']}")
|
||||
print(f" - Failed downloads: {summary['failed_downloads']}")
|
||||
print(f" - Total size: {summary['total_downloaded_size']:,} bytes")
|
||||
print(f" - Success rate: {summary['success_rate']:.1f}%")
|
||||
print(f" - Download directory: {summary['download_directory']}")
|
||||
|
||||
if result['failed_downloads']:
|
||||
print(f"\n⚠️ Failed downloads:")
|
||||
for failure in result['failed_downloads']:
|
||||
print(f" - {failure['package']}: {failure['error']}")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error downloading packages: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def analyze_small_package():
|
||||
"""Analyze a smaller package for demonstration."""
|
||||
print("\n🔍 Analyzing 'click' package dependencies...")
|
||||
|
||||
try:
|
||||
result = await resolve_package_dependencies(
|
||||
package_name="click",
|
||||
python_version="3.10",
|
||||
include_extras=[],
|
||||
include_dev=False,
|
||||
max_depth=5
|
||||
)
|
||||
|
||||
print(f"✅ Successfully resolved dependencies for {result['package_name']}")
|
||||
|
||||
# Show detailed dependency tree
|
||||
print(f"\n🌳 Dependency Tree:")
|
||||
dependency_tree = result['dependency_tree']
|
||||
|
||||
for pkg_name, pkg_info in dependency_tree.items():
|
||||
indent = " " * pkg_info['depth']
|
||||
print(f"{indent}- {pkg_info['name']} ({pkg_info['version']})")
|
||||
|
||||
runtime_deps = pkg_info['dependencies']['runtime']
|
||||
if runtime_deps:
|
||||
for dep in runtime_deps[:3]: # Show first 3 dependencies
|
||||
print(f"{indent} └─ {dep}")
|
||||
if len(runtime_deps) > 3:
|
||||
print(f"{indent} └─ ... and {len(runtime_deps) - 3} more")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error analyzing dependencies: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main demonstration function."""
|
||||
print("🚀 PyPI Query MCP Server - Dependency Analysis Demo")
|
||||
print("=" * 60)
|
||||
|
||||
# Analyze a small package first
|
||||
click_result = await analyze_small_package()
|
||||
|
||||
# Analyze PySide2 dependencies
|
||||
pyside2_result = await analyze_pyside2_dependencies()
|
||||
|
||||
# Optionally download packages (commented out to avoid large downloads in demo)
|
||||
# download_result = await download_pyside2_packages()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("✨ Demo completed!")
|
||||
|
||||
if click_result:
|
||||
print(f"📝 Click analysis saved to: click_dependencies.json")
|
||||
with open("click_dependencies.json", "w") as f:
|
||||
json.dump(click_result, f, indent=2)
|
||||
|
||||
if pyside2_result:
|
||||
print(f"📝 PySide2 analysis saved to: pyside2_dependencies.json")
|
||||
with open("pyside2_dependencies.json", "w") as f:
|
||||
json.dump(pyside2_result, f, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue