chore: upgrade all Python packages and fix linting issues

- Update all dependencies to latest versions (fastmcp, httpx, packaging, etc.)
- Downgrade click from yanked 8.2.2 to stable 8.1.7
- Fix code formatting and linting issues with ruff
- Most tests passing (2 test failures in dependency resolver need investigation)
This commit is contained in:
Ryan Malloy 2025-08-15 20:23:14 -06:00
parent 503ea589f1
commit 8b43927493
34 changed files with 2276 additions and 1593 deletions

View file

@ -3,28 +3,32 @@
import asyncio
import logging
import sys
import os
import re
import httpx
import sys
from urllib.parse import quote
import httpx
# Add the project root to the Python path
sys.path.insert(0, os.path.dirname(__file__))
# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
class SimplePackageNotFoundError(Exception):
"""Simple exception for package not found."""
pass
class SimplePyPIClient:
"""Simplified PyPI client for testing."""
def __init__(self):
self.base_url = "https://pypi.org/pypi"
self.client = httpx.AsyncClient(
@ -35,28 +39,30 @@ class SimplePyPIClient:
},
follow_redirects=True,
)
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.client.aclose()
async def get_package_info(self, package_name: str, version: str = None):
"""Get package info with optional version."""
if version:
url = f"{self.base_url}/{quote(package_name)}/{quote(version)}/json"
else:
url = f"{self.base_url}/{quote(package_name)}/json"
response = await self.client.get(url)
if response.status_code == 404:
if version:
raise SimplePackageNotFoundError(f"Version {version} not found for package {package_name}")
raise SimplePackageNotFoundError(
f"Version {version} not found for package {package_name}"
)
else:
raise SimplePackageNotFoundError(f"Package {package_name} not found")
response.raise_for_status()
return response.json()
@ -65,7 +71,7 @@ def validate_version_format(version: str | None) -> bool:
"""Validate version format."""
if version is None:
return True
version_pattern = r"^[0-9]+(?:\.[0-9]+)*(?:[\.\-]?(?:a|b|rc|alpha|beta|dev|pre|post|final)[0-9]*)*$"
return bool(re.match(version_pattern, version.strip(), re.IGNORECASE))
@ -73,49 +79,53 @@ def validate_version_format(version: str | None) -> bool:
async def test_version_parameter_fix():
"""Test the version parameter functionality."""
logger.info("Testing version parameter fix...")
async with SimplePyPIClient() as client:
# Test 1: Django 4.2.0 (specific version)
logger.info("Testing Django 4.2.0...")
try:
data = await client.get_package_info("django", "4.2.0")
actual_version = data.get("info", {}).get("version", "")
if actual_version in ["4.2", "4.2.0"]:
logger.info(f"✅ Django 4.2.0 test passed (got version: {actual_version})")
logger.info(
f"✅ Django 4.2.0 test passed (got version: {actual_version})"
)
# Check dependencies
deps = data.get("info", {}).get("requires_dist", [])
logger.info(f" Dependencies found: {len(deps) if deps else 0}")
# Print a few dependencies to show they're different from latest
if deps:
logger.info(f" Sample dependencies: {deps[:3]}")
else:
logger.error(f"❌ Expected version 4.2.0, got {actual_version}")
return False
except Exception as e:
logger.error(f"❌ Django 4.2.0 test failed: {e}")
return False
# Test 2: Django latest (no version)
logger.info("Testing Django latest...")
try:
data = await client.get_package_info("django")
latest_version = data.get("info", {}).get("version", "")
logger.info(f"✅ Django latest test passed - version: {latest_version}")
# Verify that latest != 4.2.0 (to prove we're getting different results)
if latest_version not in ["4.2", "4.2.0"]:
logger.info("✅ Confirmed: latest version is different from 4.2.0")
else:
logger.info(" Latest version happens to be 4.2.0 (unlikely but possible)")
logger.info(
" Latest version happens to be 4.2.0 (unlikely but possible)"
)
except Exception as e:
logger.error(f"❌ Django latest test failed: {e}")
return False
# Test 3: FastAPI 0.100.0
logger.info("Testing FastAPI 0.100.0...")
try:
@ -123,7 +133,7 @@ async def test_version_parameter_fix():
actual_version = data.get("info", {}).get("version", "")
if actual_version == "0.100.0":
logger.info("✅ FastAPI 0.100.0 test passed")
# Check dependencies
deps = data.get("info", {}).get("requires_dist", [])
logger.info(f" Dependencies found: {len(deps) if deps else 0}")
@ -133,7 +143,7 @@ async def test_version_parameter_fix():
except Exception as e:
logger.error(f"❌ FastAPI 0.100.0 test failed: {e}")
return False
# Test 4: NumPy 1.20.0
logger.info("Testing NumPy 1.20.0...")
try:
@ -141,7 +151,7 @@ async def test_version_parameter_fix():
actual_version = data.get("info", {}).get("version", "")
if actual_version == "1.20.0":
logger.info("✅ NumPy 1.20.0 test passed")
# Check dependencies
deps = data.get("info", {}).get("requires_dist", [])
logger.info(f" Dependencies found: {len(deps) if deps else 0}")
@ -151,7 +161,7 @@ async def test_version_parameter_fix():
except Exception as e:
logger.error(f"❌ NumPy 1.20.0 test failed: {e}")
return False
# Test 5: Non-existent version (should fail)
logger.info("Testing Django 999.999.999 (should fail)...")
try:
@ -163,7 +173,7 @@ async def test_version_parameter_fix():
except Exception as e:
logger.error(f"❌ Unexpected error type: {e}")
return False
# Test 6: Pre-release version
logger.info("Testing Django 5.0a1 (pre-release)...")
try:
@ -171,18 +181,20 @@ async def test_version_parameter_fix():
actual_version = data.get("info", {}).get("version", "")
logger.info(f"✅ Django 5.0a1 test passed - got version: {actual_version}")
except SimplePackageNotFoundError:
logger.info(" Django 5.0a1 not found (this is expected for some pre-release versions)")
logger.info(
" Django 5.0a1 not found (this is expected for some pre-release versions)"
)
except Exception as e:
logger.error(f"❌ Django 5.0a1 test failed: {e}")
return False
return True
def test_version_validation():
"""Test version validation."""
logger.info("Testing version validation...")
test_cases = [
("1.0.0", True),
("2.1", True),
@ -195,41 +207,45 @@ def test_version_validation():
("", False),
(None, True),
]
all_passed = True
for version, expected in test_cases:
result = validate_version_format(version)
if result == expected:
logger.info(f"✅ Version validation for '{version}': {result}")
else:
logger.error(f"❌ Version validation for '{version}': expected {expected}, got {result}")
logger.error(
f"❌ Version validation for '{version}': expected {expected}, got {result}"
)
all_passed = False
return all_passed
async def compare_dependencies():
"""Compare dependencies between different versions."""
logger.info("Comparing dependencies between Django versions...")
async with SimplePyPIClient() as client:
# Get Django 4.2.0 dependencies
data_420 = await client.get_package_info("django", "4.2.0")
deps_420 = data_420.get("info", {}).get("requires_dist", [])
# Get Django latest dependencies
data_latest = await client.get_package_info("django")
deps_latest = data_latest.get("info", {}).get("requires_dist", [])
logger.info(f"Django 4.2.0 dependencies: {len(deps_420) if deps_420 else 0}")
logger.info(f"Django latest dependencies: {len(deps_latest) if deps_latest else 0}")
logger.info(
f"Django latest dependencies: {len(deps_latest) if deps_latest else 0}"
)
# Show some dependencies for comparison
if deps_420:
logger.info(f"Django 4.2.0 sample deps: {deps_420[:2]}")
if deps_latest:
logger.info(f"Django latest sample deps: {deps_latest[:2]}")
# They might be the same if 4.2.0 is latest, but structure should be correct
return True
@ -237,32 +253,34 @@ async def compare_dependencies():
async def main():
"""Run all tests."""
logger.info("🧪 Starting version parameter fix verification tests...")
success = True
# Test version validation
if test_version_validation():
logger.info("✅ Version validation tests passed")
else:
logger.error("❌ Version validation tests failed")
success = False
# Test version parameter functionality
if await test_version_parameter_fix():
logger.info("✅ Version parameter fix tests passed")
else:
logger.error("❌ Version parameter fix tests failed")
success = False
# Compare dependencies
if await compare_dependencies():
logger.info("✅ Dependency comparison test passed")
else:
logger.error("❌ Dependency comparison test failed")
success = False
if success:
logger.info("🎉 All tests passed! The version parameter fix is working correctly.")
logger.info(
"🎉 All tests passed! The version parameter fix is working correctly."
)
logger.info("")
logger.info("Summary of what was fixed:")
logger.info("- PyPIClient now supports version-specific queries")
@ -277,4 +295,4 @@ async def main():
if __name__ == "__main__":
exit_code = asyncio.run(main())
sys.exit(exit_code)
sys.exit(exit_code)