# UV Integration Plan for oteltest

## Current State: How oteltest Uses pip

### Package Installation Flow

1. **Create virtual environment** (`private.py:237`)
   ```python
   venv.create(self.venv_dir, with_pip=True)
   ```

2. **Get pip path** (`private.py:83`)
   ```python
   pip_path = script_venv.path_to_executable("pip")
   # Returns: {venv_dir}/bin/pip
   ```

3. **Install packages one-by-one** (`private.py:85-87`)
   ```python
   for req in oteltest_instance.requirements():
       logger.info("Will install requirement: '%s'", req)
       run_subprocess([pip_path, "install", req], logger)
   ```

### Key Characteristics

- **Isolated venvs per test**: Each test script gets its own virtual environment
- **Individual package installation**: Packages installed one at a time, not from requirements.txt
- **Venv caching**: If venv already exists, it's reused (skips recreation)
- **Standard location**: `{venv_dir}/bin/pip` on macOS/Linux

## Why Use uv?

**uv** is a modern Python package installer written in Rust that is:
- **10-100x faster** than pip for package installation
- **Compatible** with pip command syntax
- **Drop-in replacement** for most pip workflows

### Performance Comparison

Typical oteltest run with pip (installing 3-4 packages):
- OpenTelemetry package installation: ~10-30 seconds per test
- With uv: ~1-3 seconds per test

This adds up significantly when running 30+ baseline tests.

## Implementation Plan

### Option 1: Use `uv pip` (Minimal Changes)

Replace pip commands with uv pip commands while keeping Python's venv module.

**Changes needed:**
1. Check if uv is available on system
2. Replace `[pip_path, "install", req]` with `["uv", "pip", "install", req]`
3. Keep existing venv creation logic

**Pros:**
- Minimal code changes
- Works with existing venv structure
- Easy to fall back to pip if uv not available

**Cons:**
- Still uses Python's slower venv module
- Doesn't leverage uv's full speed potential

### Option 2: Use `uv venv` + `uv pip` (Full Migration)

Replace both venv creation and pip installation with uv.

**Changes needed:**
1. Replace `venv.create()` with `uv venv` command
2. Replace `[pip_path, "install", req]` with `["uv", "pip", "install", req]`
3. Update `Venv` class to use uv

**Pros:**
- Maximum performance (uv handles both venv and packages)
- Future-proof as uv becomes standard
- Single tool for environment management

**Cons:**
- More code changes
- Requires uv to be installed
- Need fallback strategy

### Option 3: Hybrid Approach (Recommended)

Use uv if available, fall back to pip if not.

**Changes needed:**
1. Add `uv_available()` check function
2. Modify `Venv.create()` to try uv first, fall back to venv module
3. Modify install logic to use uv pip if available, regular pip otherwise
4. Add configuration option to force pip/uv

**Pros:**
- Best of both worlds
- Works for all users (with or without uv)
- Faster for users with uv installed
- Easy migration path

**Cons:**
- Slightly more complex logic
- Two code paths to maintain

## Recommended Implementation: Hybrid Approach

### Step 1: Add uv Detection

Add to `private.py`:
```python
def uv_available() -> bool:
    """Check if uv is available on the system."""
    try:
        result = subprocess.run(
            ["uv", "--version"],
            capture_output=True,
            check=True,
        )
        return result.returncode == 0
    except (subprocess.CalledProcessError, FileNotFoundError):
        return False
```

### Step 2: Update Venv Class

Modify `Venv.create()` method in `private.py:230-237`:

```python
def create(self):
    if os.path.exists(self.venv_dir):
        self.logger.info(
            "Path to virtual env [%s] already exists, skipping creation",
            self.venv_dir,
        )
    else:
        if uv_available():
            self.logger.info("Creating venv with uv")
            run_subprocess(["uv", "venv", self.venv_dir], self.logger)
        else:
            self.logger.info("Creating venv with Python venv module")
            venv.create(self.venv_dir, with_pip=True)
```

### Step 3: Update Package Installation

Modify installation logic in `private.py:83-87`:

```python
use_uv = uv_available()

if use_uv:
    logger.info("Using uv for package installation")
    for req in oteltest_instance.requirements():
        logger.info("Will install requirement: '%s'", req)
        run_subprocess(["uv", "pip", "install", "--python", script_venv.python_path(), req], logger)
else:
    logger.info("Using pip for package installation")
    pip_path = script_venv.path_to_executable("pip")
    for req in oteltest_instance.requirements():
        logger.info("Will install requirement: '%s'", req)
        run_subprocess([pip_path, "install", req], logger)
```

### Step 4: Add Python Path Helper

Add to `Venv` class:
```python
def python_path(self):
    """Return path to Python executable in venv."""
    return self.path_to_executable("python")
```

### Step 5: Update Documentation

Add to README.md:
```markdown
## Performance: Using uv (Optional)

For faster package installation, install [uv](https://github.com/astral-sh/uv):

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

oteltest will automatically detect and use uv if available, resulting in 10-100x faster package installation.

To force using pip even if uv is available:
```bash
OTELTEST_USE_PIP=1 oteltest my_test.py
```
```

## Testing Strategy

1. **Test with uv installed**: Verify uv is used and tests run correctly
2. **Test without uv**: Verify fallback to pip works
3. **Test venv caching**: Verify existing venvs are reused correctly
4. **Test error handling**: Verify uv failures fall back to pip gracefully

## File Changes Summary

### Files to modify:
1. **`src/oteltest/private.py`**
   - Add `uv_available()` function
   - Modify `Venv.create()` method
   - Modify package installation loop (lines 83-87)
   - Add `Venv.python_path()` method

2. **`README.md`**
   - Add optional uv installation section
   - Document performance benefits

### Files to create:
- None (all changes in existing files)

## Implementation Phases

### Phase 1: Basic uv Detection and Usage
- Add uv detection
- Use uv pip install if available
- Keep venv module for venv creation
- **Estimated time:** 1-2 hours

### Phase 2: Full uv Integration
- Use uv venv for environment creation
- Optimize uv flags for speed
- **Estimated time:** 1 hour

### Phase 3: Configuration and Polish
- Add environment variable to force pip
- Update documentation
- Add logging for which tool is used
- **Estimated time:** 1 hour

## Benefits Summary

### Before (pip):
```
Test run time: ~30 seconds
- Venv creation: ~5 seconds
- Package installation: ~25 seconds (3-4 packages)
- Script execution: ~2 seconds
```

### After (uv):
```
Test run time: ~8 seconds
- Venv creation: ~1 second
- Package installation: ~3 seconds (3-4 packages)
- Script execution: ~2 seconds
```

**Savings: ~22 seconds per test × 30 tests = 11 minutes saved on full baseline suite**

## Next Steps

1. Implement Phase 1 (basic uv detection and usage)
2. Test with existing baseline test
3. Verify fallback to pip works
4. Run full test suite
5. Iterate based on results
