CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-lastversion

A CLI tool to find the latest stable version of an arbitrary project

Pending
Overview
Eval results
Files

exceptions.mddocs/

Exception Handling

Custom exception classes for specific error conditions encountered during version discovery and file operations. These exceptions provide structured error handling and enable applications to respond appropriately to different failure scenarios.

Capabilities

API and Authentication Errors

Exception classes for handling API-related errors and authentication failures across different platforms.

class ApiCredentialsError(Exception):
    """
    Raised when there's an API error related to credentials or authentication.
    
    This exception occurs when:
    - API tokens are invalid or expired
    - Rate limits are exceeded due to unauthenticated requests
    - Private repositories require authentication
    - API endpoints require elevated permissions
    
    Common scenarios:
    - GitHub API rate limiting without token
    - GitLab private project access
    - Enterprise instance authentication failures
    """
    
    def __init__(self, message: str, platform: str = None):
        """
        Initialize API credentials error.
        
        Parameters:
        - message: Descriptive error message
        - platform: Platform where error occurred (github, gitlab, etc.)
        """

Project and Repository Errors

Exception for handling cases where specified projects or repositories cannot be found or accessed.

class BadProjectError(Exception):
    """
    Raised when no such project exists or cannot be accessed.
    
    This exception occurs when:
    - Repository does not exist on specified platform
    - Project has been deleted or made private
    - Invalid repository identifier format
    - Platform does not host the specified project
    - Network issues prevent project access
    
    Common scenarios:
    - Typos in repository names
    - Projects moved between platforms
    - Private repositories without access
    - Deleted or archived projects
    """
    
    def __init__(self, message: str, repo: str = None):
        """
        Initialize bad project error.
        
        Parameters:
        - message: Descriptive error message
        - repo: Repository identifier that caused the error
        """

Security and File Operation Errors

Exception for security-related errors during file operations, particularly archive extraction.

class TarPathTraversalException(Exception):
    """
    Custom exception for path traversal attempts during tar extraction.
    
    This exception is raised when archive extraction detects potentially
    malicious archive contents that attempt to write files outside the
    intended extraction directory.
    
    Security concerns addressed:
    - Path traversal attacks using "../" sequences
    - Absolute path entries in archives
    - Symbolic link attacks
    - Archive bombs (deeply nested directories)
    
    Prevention measures:
    - All paths are validated before extraction
    - Relative paths are enforced
    - Symbolic links are restricted or resolved safely
    """
    
    def __init__(self, message: str, path: str = None):
        """
        Initialize path traversal exception.
        
        Parameters:
        - message: Descriptive error message
        - path: Problematic path that triggered the security check
        """

Usage Examples

API Credentials Error Handling

from lastversion import latest
from lastversion.exceptions import ApiCredentialsError

def get_version_with_auth_handling(repo):
    """Get version with proper authentication error handling."""
    try:
        return latest(repo)
    except ApiCredentialsError as e:
        print(f"Authentication required for {repo}")
        print("To resolve:")
        print("1. Set GITHUB_TOKEN environment variable")
        print("2. Use personal access token for private repos")
        print(f"Error details: {e}")
        return None

# Example usage
version = get_version_with_auth_handling("private-org/private-repo")
if version:
    print(f"Version: {version}")
else:
    print("Could not retrieve version due to authentication issues")

Project Not Found Handling

from lastversion import latest
from lastversion.exceptions import BadProjectError

def find_project_across_platforms(project_name):
    """Try to find project across multiple platforms."""
    platforms = ['github', 'gitlab', 'pip', 'sf']
    
    for platform in platforms:
        try:
            version = latest(project_name, at=platform)
            if version:
                print(f"Found {project_name} on {platform}: {version}")
                return version, platform
        except BadProjectError:
            print(f"{project_name} not found on {platform}")
            continue
        except Exception as e:
            print(f"Error checking {platform}: {e}")
            continue
    
    raise BadProjectError(f"Project {project_name} not found on any platform")

# Example usage
try:
    version, platform = find_project_across_platforms("some-project")
    print(f"Successfully found project on {platform}")
except BadProjectError as e:
    print(f"Project search failed: {e}")

Safe Archive Extraction

from lastversion.utils import extract_file
from lastversion.exceptions import TarPathTraversalException
import os

def safe_extract_with_error_handling(url, extract_dir):
    """Safely extract archive with comprehensive error handling."""
    try:
        # Ensure extraction directory exists
        os.makedirs(extract_dir, exist_ok=True)
        
        # Attempt extraction
        result = extract_file(url, extract_dir)
        print(f"Successfully extracted to {extract_dir}")
        return result
        
    except TarPathTraversalException as e:
        print(f"Security error: Archive contains unsafe paths")
        print(f"Blocked path: {e}")
        print("This archive may be malicious and was not extracted")
        return None
        
    except Exception as e:
        print(f"Extraction failed: {e}")
        return None

# Example usage
url = "https://example.com/suspicious-archive.tar.gz"
result = safe_extract_with_error_handling(url, "/tmp/safe-extract")

Comprehensive Error Handling

from lastversion import latest, has_update
from lastversion.exceptions import (
    ApiCredentialsError, 
    BadProjectError, 
    TarPathTraversalException
)
from packaging.version import InvalidVersion

def robust_version_check(repo, current_version=None):
    """Comprehensive version checking with full error handling."""
    try:
        # Get latest version
        latest_version = latest(repo)
        
        if current_version:
            # Check for updates
            update = has_update(repo, current_version)
            return {
                'latest': latest_version,
                'current': current_version,
                'update_available': bool(update),
                'newer_version': update if update else None,
                'status': 'success'
            }
        else:
            return {
                'latest': latest_version,
                'status': 'success'
            }
            
    except ApiCredentialsError as e:
        return {
            'status': 'auth_error',
            'error': str(e),
            'suggestion': 'Set appropriate API token environment variable'
        }
        
    except BadProjectError as e:
        return {
            'status': 'not_found',
            'error': str(e),
            'suggestion': 'Check repository name and platform'
        }
        
    except InvalidVersion as e:
        return {
            'status': 'version_error', 
            'error': f"Invalid version format: {e}",
            'suggestion': 'Check version string format'
        }
        
    except Exception as e:
        return {
            'status': 'unknown_error',
            'error': str(e),
            'suggestion': 'Check network connectivity and repository access'
        }

# Example usage
result = robust_version_check("kubernetes/kubernetes", "1.28.0")

if result['status'] == 'success':
    print(f"Latest: {result['latest']}")
    if result.get('update_available'):
        print(f"Update available: {result['current']} → {result['newer_version']}")
else:
    print(f"Error ({result['status']}): {result['error']}")
    print(f"Suggestion: {result['suggestion']}")

Exception Chaining and Context

from lastversion import latest
from lastversion.exceptions import BadProjectError, ApiCredentialsError

class VersionDiscoveryError(Exception):
    """High-level exception for version discovery failures."""
    pass

def enterprise_version_check(internal_repo, fallback_repo=None):
    """Check enterprise repository with fallback handling."""
    try:
        # Try internal/enterprise repository first
        version = latest(internal_repo, at='github')
        return version, 'enterprise'
        
    except ApiCredentialsError as e:
        print(f"Enterprise auth failed: {e}")
        
        if fallback_repo:
            try:
                # Fallback to public repository
                version = latest(fallback_repo)
                return version, 'public_fallback'
            except BadProjectError as fallback_error:
                # Chain exceptions to preserve error context
                raise VersionDiscoveryError(
                    f"Both enterprise and fallback failed"
                ) from fallback_error
        else:
            raise VersionDiscoveryError(
                "Enterprise access failed and no fallback configured"
            ) from e
            
    except BadProjectError as e:
        raise VersionDiscoveryError(
            f"Enterprise repository not found: {internal_repo}"
        ) from e

# Example usage with exception chaining
try:
    version, source = enterprise_version_check(
        "internal/secret-project",
        "public/open-project"
    )
    print(f"Version {version} from {source}")
    
except VersionDiscoveryError as e:
    print(f"Version discovery failed: {e}")
    # Access original exception via __cause__
    if e.__cause__:
        print(f"Root cause: {e.__cause__}")

Exception-Based Control Flow

from lastversion import latest
from lastversion.exceptions import BadProjectError, ApiCredentialsError

def get_version_with_retries(repo, max_retries=3):
    """Get version with retry logic based on exception types."""
    import time
    
    for attempt in range(max_retries):
        try:
            return latest(repo)
            
        except ApiCredentialsError:
            # Don't retry auth errors
            raise
            
        except BadProjectError:
            # Don't retry not found errors
            raise
            
        except Exception as e:
            # Retry network/temporary errors
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt  # Exponential backoff
                print(f"Attempt {attempt + 1} failed: {e}")
                print(f"Retrying in {wait_time} seconds...")
                time.sleep(wait_time)
            else:
                print(f"All {max_retries} attempts failed")
                raise

# Example usage
try:
    version = get_version_with_retries("kubernetes/kubernetes")
    print(f"Retrieved version: {version}")
except (BadProjectError, ApiCredentialsError) as e:
    print(f"Permanent error: {e}")
except Exception as e:
    print(f"Failed after retries: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-lastversion

docs

core-functions.md

exceptions.md

index.md

repository-holders.md

utilities.md

version-handling.md

tile.json