RobotPai / scripts /verify_requirements.py
atr0p05's picture
Upload 294 files
78402dd verified
#!/usr/bin/env python3
"""
Requirements Version Verifier
This script checks if all packages and versions in a requirements.txt file actually exist on PyPI.
It helps prevent runtime errors from non-existent package versions.
"""
import sys
import requests
import re
from typing import List, Tuple, Dict
import json
def parse_requirements(requirements_text: str) -> List[Tuple[str, str]]:
"""Parse requirements.txt content and extract package names and versions."""
packages = []
for line in requirements_text.strip().split('\n'):
line = line.strip()
# Skip comments and empty lines
if not line or line.startswith('#') or line.startswith('=='):
continue
# Extract package name and version
if '==' in line:
parts = line.split('==')
if len(parts) == 2:
package_name = parts[0].strip()
version = parts[1].strip()
packages.append((package_name, version))
elif '>=' in line or '<=' in line or '~=' in line:
# Handle other version specifiers
match = re.match(r'^([a-zA-Z0-9-_.]+)\s*([><=~]+)\s*([\d.]+.*)', line)
if match:
package_name = match.group(1)
operator = match.group(2)
version = match.group(3)
packages.append((package_name, f"{operator}{version}"))
return packages
def check_package_version(package_name: str, version: str) -> Dict[str, any]:
"""Check if a specific package version exists on PyPI."""
try:
# Get package info from PyPI
response = requests.get(f"https://pypi.org/pypi/{package_name}/json", timeout=10)
if response.status_code == 404:
return {
"exists": False,
"error": "Package not found on PyPI",
"available_versions": []
}
if response.status_code != 200:
return {
"exists": False,
"error": f"HTTP {response.status_code}: {response.reason}",
"available_versions": []
}
data = response.json()
available_versions = list(data.get("releases", {}).keys())
# Check if the specific version exists
if version.startswith('=='):
version_check = version[2:]
else:
version_check = version
if version_check in available_versions:
return {
"exists": True,
"available_versions": sorted(available_versions, reverse=True)[:10] # Show latest 10
}
else:
return {
"exists": False,
"error": f"Version {version_check} not found",
"available_versions": sorted(available_versions, reverse=True)[:10]
}
except requests.exceptions.RequestException as e:
return {
"exists": False,
"error": f"Network error: {str(e)}",
"available_versions": []
}
except json.JSONDecodeError:
return {
"exists": False,
"error": "Invalid response from PyPI",
"available_versions": []
}
def verify_requirements(requirements_text: str) -> Dict[str, any]:
"""Verify all packages in requirements text."""
packages = parse_requirements(requirements_text)
results = {
"total": len(packages),
"valid": 0,
"invalid": 0,
"errors": []
}
print(f"Checking {len(packages)} packages...")
print("-" * 70)
for package_name, version in packages:
result = check_package_version(package_name, version)
if result["exists"]:
results["valid"] += 1
print(f"✅ {package_name}=={version}")
else:
results["invalid"] += 1
error_info = {
"package": package_name,
"version": version,
"error": result["error"],
"available_versions": result["available_versions"][:5] # Show top 5
}
results["errors"].append(error_info)
print(f"❌ {package_name}=={version}")
print(f" Error: {result['error']}")
if result["available_versions"]:
print(f" Available versions: {', '.join(result['available_versions'][:5])}")
print()
return results
def suggest_fixes(errors: List[Dict]) -> None:
"""Suggest fixes for invalid packages."""
if not errors:
return
print("\n" + "=" * 70)
print("SUGGESTED FIXES:")
print("=" * 70)
for error in errors:
package = error["package"]
requested_version = error["version"]
available = error["available_versions"]
print(f"\n{package}=={requested_version}")
if available:
# Find the closest available version
if requested_version.replace('==', '') in [v.split('rc')[0].split('b')[0].split('a')[0] for v in available]:
# Pre-release version might exist
print(f" → Try: {package}=={available[0]}")
else:
# Suggest the latest stable version
stable_versions = [v for v in available if not any(x in v for x in ['rc', 'a', 'b', 'dev'])]
if stable_versions:
print(f" → Try: {package}=={stable_versions[0]}")
else:
print(f" → Try: {package}=={available[0]}")
else:
print(f" → Package might not exist. Check the package name.")
def main():
"""Main function to verify requirements file."""
if len(sys.argv) > 1:
# Read from file
filename = sys.argv[1]
try:
with open(filename, 'r') as f:
requirements_text = f.read()
except FileNotFoundError:
print(f"Error: File '{filename}' not found")
sys.exit(1)
else:
# Read from stdin or use the example
print("Paste your requirements.txt content (press Ctrl+D when done):")
try:
requirements_text = sys.stdin.read()
except KeyboardInterrupt:
print("\nCancelled")
sys.exit(0)
results = verify_requirements(requirements_text)
print("\n" + "=" * 70)
print("SUMMARY:")
print("=" * 70)
print(f"Total packages: {results['total']}")
print(f"Valid packages: {results['valid']} ✅")
print(f"Invalid packages: {results['invalid']} ❌")
if results["errors"]:
suggest_fixes(results["errors"])
sys.exit(1)
else:
print("\nAll packages and versions are valid! 🎉")
sys.exit(0)
if __name__ == "__main__":
main()