An interactive pip requirements upgrader that also updates the version in your requirements.txt file
—
PyPI API integration for checking package upgrade availability, supporting both JSON API and simple HTML formats, with custom index URL handling.
Queries PyPI or custom package indexes to detect available upgrades for packages from requirements files.
class PackagesStatusDetector:
def __init__(self, packages, use_default_index=False):
"""
Initialize package status detector.
Args:
packages (list): List of package specification strings from requirements files
use_default_index (bool): If True, skip custom index URL detection and use PyPI
"""
def detect_available_upgrades(self, options):
"""
Detect available upgrades for all packages.
Args:
options (dict): Options dictionary containing:
--prerelease (bool): Include prerelease versions
-p (list): List of explicitly selected packages or ['all']
Returns:
dict: Package status map with package names as keys and status dicts as values
"""Automatically detects custom PyPI index URLs from pip configuration files and environment variables.
def _update_index_url_from_configs(self):
"""
Check for alternative index-url in pip configuration files.
Priority order:
1. PIP_INDEX_URL environment variable
2. pip.conf/pip.ini files in various locations:
- ~/.pip/pip.conf, ~/.pip/pip.ini
- ~/.config/pip/pip.conf, ~/.config/pip/pip.ini
- Virtual environment pip.conf, pip.ini
- System-wide pip configuration files
Updates self.PYPI_API_URL and self.PYPI_API_TYPE accordingly.
"""
def _prepare_api_url(self, index_url):
"""
Prepare API URL based on index URL format.
Args:
index_url (str): Base index URL from configuration
Returns:
str: API URL template for package queries
Supported formats:
- Standard PyPI JSON API: /pypi/{package}/json
- Simple HTML API: /simple/{package}/
- Custom formats with /pypi/ in path
"""Fetches package information from PyPI or custom indexes with timeout and error handling.
def _fetch_index_package_info(self, package_name, current_version):
"""
Fetch package information from the configured index.
Args:
package_name (str): Name of the package to query
current_version (packaging.version.Version): Current version from requirements
Returns:
tuple: (package_status_dict, reason) where:
package_status_dict (dict or False): Package status information or False on error
reason (str): Success message or error description
Network configuration:
- 15 second timeout for HTTP requests
- Handles HTTPError and connection issues
- Uses canonical package names for simple HTML API
"""Parses package specification lines to extract package names and versions.
def _expand_package(self, package_line):
"""
Extract package name and version from requirements line.
Args:
package_line (str): Package specification (e.g., "django==3.2.0")
Returns:
tuple: (package_name, version) or (None, None) if not parseable
Supports:
- Exact version pins: package==1.0.0
- Package extras: package[extra1,extra2]==1.0.0
- Filters out packages without == specifications
"""Default format using PyPI's JSON API endpoint.
def _parse_pypi_json_package_info(self, package_name, current_version, response):
"""
Parse PyPI JSON API response for package information.
Args:
package_name (str): Package name
current_version (packaging.version.Version): Current version
response (requests.Response): HTTP response from PyPI JSON API
Returns:
tuple: (package_status_dict, reason)
JSON API features:
- Full version history in releases dict
- Upload timestamps for each release
- Package metadata and description
- Handles prerelease and postrelease versions
"""Fallback format for custom package indexes that provide simple HTML listing.
def _parse_simple_html_package_info(self, package_name, current_version, response):
"""
Parse simple HTML API response for package information.
Args:
package_name (str): Package name
current_version (packaging.version.Version): Current version
response (requests.Response): HTTP response from simple HTML API
Returns:
tuple: (package_status_dict, reason)
HTML API features:
- Regex parsing of package links
- Version extraction from filenames
- No upload timestamp information (returns '-')
- Handles prerelease versions through version parsing
"""export PIP_INDEX_URL="https://pypi.company.com/simple/"Takes highest priority over configuration files.
Searched in order of priority:
~/.pip/pip.conf # User-specific (Unix)
~/.pip/pip.ini # User-specific (Windows)
~/.config/pip/pip.conf # XDG config (Unix)
~/.config/pip/pip.ini # XDG config (Windows)
$VIRTUAL_ENV/pip.conf # Virtual environment
$VIRTUAL_ENV/pip.ini # Virtual environment
[system locations] # System-wide configurationsConfiguration format:
[global]
index-url = https://pypi.company.com/simple/from pip_upgrader.packages_status_detector import PackagesStatusDetector
from packaging import version
# Initialize with packages from requirements files
packages = ['django==3.2.0', 'requests==2.25.1']
detector = PackagesStatusDetector(packages)
# Check for upgrades
options = {'--prerelease': False, '-p': []}
status_map = detector.detect_available_upgrades(options)
# Example output
print(status_map)
{
'django': {
'name': 'django',
'current_version': Version('3.2.0'),
'latest_version': Version('4.1.0'),
'upgrade_available': True,
'upload_time': '2022-08-03 08:15:30'
},
'requests': {
'name': 'requests',
'current_version': Version('2.25.1'),
'latest_version': Version('2.25.1'),
'upgrade_available': False,
'upload_time': '2021-01-05 14:22:18'
}
}# Use custom index from environment or config
detector = PackagesStatusDetector(packages, use_default_index=False)
# Force default PyPI index
detector = PackagesStatusDetector(packages, use_default_index=True)# Include prerelease versions
options = {'--prerelease': True, '-p': []}
status_map = detector.detect_available_upgrades(options)
# This might return version 4.2.0rc1 instead of 4.1.0 for django# Only check specific packages
options = {'--prerelease': False, '-p': ['django', 'flask']}
status_map = detector.detect_available_upgrades(options)
# Check all packages
options = {'--prerelease': False, '-p': ['all']}
status_map = detector.detect_available_upgrades(options)Each package in the returned status map contains:
{
'name': str, # Package name as specified in requirements
'current_version': Version, # Current version from requirements file
'latest_version': Version, # Latest available version from index
'upgrade_available': bool, # True if latest > current
'upload_time': str # Upload timestamp (JSON API) or '-' (HTML API)
}Uses the packaging library for robust version comparison:
from packaging import version
current = version.parse('1.0.0')
latest = version.parse('1.1.0')
upgrade_available = current < latest # TrueInstall with Tessl CLI
npx tessl i tessl/pypi-pip-upgrader