Skip to content

refactor: import error log adding and circular import fix#484

Merged
DoganK01 merged 3 commits intomasterfrom
models-update
Nov 25, 2025
Merged

refactor: import error log adding and circular import fix#484
DoganK01 merged 3 commits intomasterfrom
models-update

Conversation

@DoganK01
Copy link
Copy Markdown
Member

  • Add import error log
  • Fix circular import

@claude
Copy link
Copy Markdown

claude bot commented Nov 25, 2025

Pull Request Review

Summary

This PR addresses two important issues:

  1. Import error handling improvement: Replaces simple ImportError exceptions with a more user-friendly import_error() function
  2. Circular import fix: Changes absolute imports to relative imports for model-related modules

Positive Aspects

1. Better User Experience

The new import_error() function (from upsonic.utils.printing) provides:

  • Formatted, user-friendly error messages with rich formatting
  • Clear install commands for missing packages
  • Context about which feature requires the package
  • Still raises ImportError after displaying the message (line 1355 in printing.py), so the behavior is functionally equivalent

2. Circular Import Fix

Changing from:

from upsonic.models import Model, ModelRequestParameters, ...

to:

from . import Model, ModelRequestParameters, ...

This is a correct solution for circular imports within the models package since:

  • The model files (anthropic.py, openai.py, etc.) are in the same package as __init__.py
  • Relative imports avoid re-importing the entire module tree
  • Follows Python best practices for intra-package imports

3. Consistency

All affected model files receive the same treatment, ensuring uniformity across the codebase.


⚠️ Concerns & Recommendations

1. Critical: Exception Chaining Lost

Before:

except ImportError as _import_error:
    raise ImportError('Please install...') from _import_error

After:

except ImportError:
    import_error(package_name="...", ...)

Issue: The exception chain (from _import_error) is lost. This is important for debugging because it shows:

  • The original import failure location
  • The full traceback
  • Which specific sub-dependency failed (if it's a transitive import error)

Recommendation: Modify import_error() to accept an optional exception parameter and re-raise with chaining, or capture and log the original exception before calling import_error().

2. Import Order Issue

The import_error function is imported inside the exception handler:

except ImportError:
    from upsonic.utils.printing import import_error
    import_error(...)

Potential Issue: If upsonic.utils.printing itself has import issues (e.g., missing rich library), this could cause a confusing nested ImportError.

Recommendation: Consider importing import_error at the top of the file, or add proper error handling if the import of import_error itself fails.

3. Testing Gap

Questions to consider:

  • Are there unit tests that verify these import errors are raised correctly?
  • Do tests verify that the circular import issue is actually resolved?
  • Are there integration tests that check behavior when optional dependencies are missing?

Recommendation: Add tests like:

def test_missing_anthropic_import():
    # Mock anthropic as missing
    with pytest.raises(ImportError, match="anthropic"):
        from upsonic.models.anthropic import AnthropicModel

4. Minor: Inconsistent Comment Style

In mistral.py, the comment changed from:

except ImportError as e:  # pragma: lax no cover

to:

except ImportError:  # pragma: lax no cover

The pragma comment is preserved, which is good! Just noting that the variable name was removed.


🔍 Security Review

✅ No security concerns identified. The changes don't introduce any new attack vectors.


🚀 Performance Considerations

✅ Minimal performance impact. The import_error() call only executes on import failures (exceptional path).


📋 Suggested Improvements

High Priority:

  1. Preserve exception chaining - This is critical for debugging

Medium Priority:

  1. Move import to top of file or add fallback handling
  2. Add unit tests for import error scenarios

Example Fix for Exception Chaining:

except ImportError as _import_error:
    from upsonic.utils.printing import import_error
    import_error(
        package_name="anthropic",
        install_command="pip install anthropic",
        feature_name="Anthropic model"
    )
    # Note: import_error already raises, but if you want to preserve chain:
    # raise ImportError(f"Missing required package: anthropic") from _import_error

Or modify import_error() signature to:

def import_error(
    package_name: str, 
    install_command: str = None, 
    feature_name: str = None,
    original_error: Exception = None  # NEW
) -> None:
    # ... display logic ...
    if original_error:
        raise ImportError(f"Missing required package: {package_name}") from original_error
    else:
        raise ImportError(f"Missing required package: {package_name}")

Overall Assessment

Code Quality: 7/10

  • Good refactoring for UX improvement
  • Correct fix for circular imports
  • Loss of exception chaining is a significant debugging concern

Recommendation: Approve with changes requested

  • The circular import fix is solid and should be merged
  • The improved error messages are great for users
  • However, preserving exception chains is important for maintainability

Once exception chaining is preserved, this would be a solid improvement to the codebase! 🚀

@claude
Copy link
Copy Markdown

claude bot commented Nov 25, 2025

PR Review: Import Error Logging and Circular Import Fix

Overview

This PR addresses two important issues:

  1. Improves error handling for missing optional dependencies across model providers
  2. Adds a new zip command to the CLI for context archiving
  3. Fixes circular import issues by using relative imports

✅ Positive Changes

1. Improved Error Handling

  • Replacing raise ImportError with import_error() utility provides better UX
  • Consistent error messaging across all model providers (Anthropic, Cohere, Google, Groq, HuggingFace, Mistral, OpenAI, Outlines)
  • Users get clear install commands instead of stack traces

2. Circular Import Fix

  • Changed from from upsonic.models import ... to from . import ... in model files
  • This is the correct pattern for sibling imports within the same package
  • Prevents potential circular dependency issues

3. New CLI Feature

  • zip command adds useful functionality for context archiving
  • Includes proper error handling and progress information

⚠️ Issues & Concerns

1. Critical: Import Error Behavior Change

Location: All model files (anthropic.py, cohere.py, google.py, etc.)

# Before
except ImportError as _import_error:
    raise ImportError(...) from _import_error

# After  
except ImportError:
    from upsonic.utils.printing import import_error
    import_error(...)

Problem: The new import_error() function doesn't raise an exception - it just prints a message. This means:

  • Code continues executing after missing imports
  • Will crash with NameError when trying to use undefined types
  • Breaks proper exception handling in calling code

Example: In src/upsonic/models/anthropic.py:118-123, if anthropic isn't installed, the code will print an error but continue, then crash when trying to use LatestAnthropicModelNames = ModelParam because ModelParam is undefined.

Fix: The import_error() function should raise an exception after logging:

def import_error(...):
    # ... print error panel ...
    raise ImportError(f"Please install {package_name}...")

2. Code Quality: Unnecessary Nested Import

Location: All model files

The pattern of importing import_error inside the exception handler is inefficient:

except ImportError:
    from upsonic.utils.printing import import_error  # Imported on every missing dependency
    import_error(...)

Recommendation: Import at the top of the file:

from upsonic.utils.printing import import_error

3. Security: Zip Command Concerns

Location: src/upsonic/cli/commands.py:850-935

  • No size limits: Could create extremely large zip files consuming disk space
  • No .gitignore respect: Will include .git/, node_modules/, virtual environments, etc.
  • Potential data leakage: May include sensitive files (.env, credentials, secrets)
  • No exclusion list: Should exclude common patterns

Recommendation: Add:

EXCLUDE_PATTERNS = [
    '.git', '.env', 'node_modules', '__pycache__', '*.pyc',
    'venv', '.venv', '*.log', '.DS_Store', 'upsonic_context_*.zip'
]

4. Missing Tests

  • No tests for the new zip_command functionality
  • No tests for the import_error() utility function
  • No tests verifying model imports still work correctly

5. Documentation Gap

  • New zip command needs documentation
  • Should explain what gets included/excluded
  • Should mention use cases

🐛 Potential Bugs

  1. Zip overwrites without warning: If upsonic_context_<timestamp>.zip exists, it's silently overwritten
  2. Race condition: Multiple simultaneous zip commands could have timestamp collisions
  3. Memory usage: Large files are loaded entirely into memory during zipping

🎯 Recommendations

High Priority (Blocking)

  1. Fix import_error() to raise exception after printing
  2. Add exclusion patterns to zip command to prevent security issues

Medium Priority

  1. Add unit tests for new functionality
  2. Move import_error import to top of model files
  3. Add size limits/warnings to zip command
  4. Respect .gitignore in zip command

Low Priority

  1. Add --exclude flag to zip command for custom patterns
  2. Add documentation for new CLI command
  3. Consider compression level options

📝 Suggested Implementation

# In src/upsonic/utils/printing.py
def import_error(package_name: str, install_command: str = None, feature_name: str = None) -> None:
    """Prints error and raises ImportError"""
    # ... existing print logic ...
    raise ImportError(
        f"Please install '{package_name}' to use {feature_name or 'this feature'}. "
        f"Run: {install_command or f'pip install {package_name}'}"
    )

📊 Test Coverage Needed

# tests/unit_tests/test_cli_zip.py
def test_zip_command_creates_archive():
    """Test basic zip creation"""
    
def test_zip_excludes_sensitive_files():
    """Test .env and .git are excluded"""
    
def test_zip_handles_large_directories():
    """Test performance with many files"""

# tests/unit_tests/test_import_errors.py  
def test_import_error_raises_exception():
    """Verify import_error raises ImportError"""
    
def test_model_import_fails_gracefully():
    """Test models handle missing dependencies correctly"""

Summary

Approve with changes required. The direction is good (better error messages, fixing circular imports), but the implementation has a critical bug where exceptions aren't raised. The zip feature is useful but needs security hardening.

Estimated effort to fix: ~2-3 hours

  • Fix import_error to raise (15 min)
  • Add exclusion patterns (30 min)
  • Write tests (1-2 hours)

Please address the critical import error behavior before merging.


Review generated with attention to Upsonic's architecture and best practices per CLAUDE.md

@DoganK01 DoganK01 merged commit 0962948 into master Nov 25, 2025
1 of 4 checks passed
@DoganK01 DoganK01 deleted the models-update branch November 25, 2025 20:03
DoganK01 added a commit that referenced this pull request Jan 5, 2026
refactor: import error log adding and circular import fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant