# Type Checking with mypy

This document describes how to use mypy for type checking in the Pinecone Python SDK.

## Overview

The SDK uses strict mypy type checking to catch errors at development time. The configuration is in `mypy.ini` at the project root.

## Running mypy

### Check all code

```bash
mypy pinecone/
```

### Check specific file or module

```bash
mypy pinecone/_internal/http/client.py
mypy pinecone._internal.http
```

### Check with verbose output

```bash
mypy --verbose pinecone/
```

## Strict Mode

The SDK runs mypy in strict mode with the following key settings:

- **`strict = True`**: Enables all strict checks
- **`warn_return_any = True`**: Warns when returning `Any` types
- **`disallow_any_generics = True`**: Requires type parameters for generic types
- **`no_implicit_reexport = True`**: Requires explicit `__all__` for re-exports
- **`warn_unreachable = True`**: Warns about unreachable code
- **`warn_unused_ignores = True`**: Warns about unnecessary `type: ignore` comments

See `mypy.ini` for the complete configuration.

## Handling Type Errors

### 1. Fix the Type Error (Preferred)

The best approach is to add proper type annotations:

```python
# Before
def process_data(data):
    return data.upper()

# After
def process_data(data: str) -> str:
    return data.upper()
```

### 2. Add Type Annotations to Third-Party Code

If a third-party library is missing types, create a stub file:

```python
# stubs/untyped_package/__init__.pyi
def some_function(x: int) -> str: ...
```

Then add to `mypy.ini`:

```ini
[mypy]
mypy_path = stubs
```

### 3. Ignore Missing Imports (Last Resort)

If a third-party package has no types and you can't create stubs, add to `mypy.ini`:

```ini
[mypy-package_name.*]
ignore_missing_imports = True
# Justification: Package does not ship type stubs and no types-* package exists
```

Always document WHY you're ignoring imports.

### 4. Inline Type Ignores (Use Sparingly)

For specific lines that are difficult to type, use inline comments with explanations:

```python
result = some_dynamic_function()  # type: ignore[no-untyped-call]  # third-party has no types
```

**Rules for type: ignore:**
- Always include the specific error code: `# type: ignore[error-code]`
- Always add a comment explaining WHY: `# type: ignore[error-code]  # explanation`
- Never use bare `# type: ignore` without an error code
- Prefer fixing the type error over ignoring it

### 5. TYPE_CHECKING Block

For imports only used in type hints (avoiding circular imports):

```python
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pinecone._internal.http.client import HTTPClient

def create_client() -> "HTTPClient":
    from pinecone._internal.http.client import HTTPClient
    return HTTPClient()
```

## Common Patterns

### Generic Types

Always provide type parameters:

```python
# Bad
def process_items(items: list) -> dict:
    ...

# Good
def process_items(items: list[str]) -> dict[str, int]:
    ...
```

### Protocol Types

Use protocols for interface definitions:

```python
from typing import Protocol

class HTTPClientProtocol(Protocol):
    def request(self, method: str, url: str) -> dict[str, Any]:
        ...
```

### Optional vs. Union[T, None]

In Python 3.10+, use `| None` syntax:

```python
# Modern (Python 3.10+)
def process(data: str | None) -> int | None:
    ...

# Legacy (still works)
from typing import Optional
def process(data: Optional[str]) -> Optional[int]:
    ...
```

### Any Type (Avoid)

Avoid `Any` type when possible. If you must use it, document why:

```python
from typing import Any

def handle_legacy_data(data: Any) -> dict[str, str]:
    """Process legacy untyped data.
    
    Args:
        data: Untyped data from legacy API. Using Any because the
              API returns dynamic structures that change per request.
    """
    ...
```

## Test Code Relaxation

Test files have relaxed type checking (see `mypy.ini`):

- `disallow_untyped_defs = False`
- `disallow_untyped_calls = False`

This allows test fixtures and mocks without strict typing while keeping production code type-safe.

## Integration with Development Workflow

### Pre-commit Hooks

Mypy runs automatically via pre-commit hooks. To skip (not recommended):

```bash
git commit --no-verify
```

### CI Pipeline

Mypy runs in CI for all pull requests. PRs with type errors will not merge.

### IDE Integration

Most Python IDEs support mypy:

- **VS Code**: Install the Pylance extension
- **PyCharm**: Built-in mypy support
- **Vim/Neovim**: Use ale or coc-pyright
- **Cursor**: Built-in Python support with mypy

## Troubleshooting

### "No module named X"

1. Ensure the module is in your PYTHONPATH
2. Check if you're in the virtual environment: `which python`
3. Verify installation: `pip list | grep X`

### "Cannot find implementation or library stub"

The package doesn't have type information:

1. Check if a `types-*` package exists: `pip search types-packagename`
2. If not, add to mypy.ini as documented above

### "Incompatible types in assignment"

The types don't match. Check:

1. Variable type annotation
2. Function return type
3. Whether you need a type cast: `cast(TargetType, value)`

### Mypy cache issues

Clear the cache if you see stale errors:

```bash
rm -rf .mypy_cache/
mypy pinecone/
```

## References

- [mypy documentation](https://mypy.readthedocs.io/)
- [PEP 484 - Type Hints](https://www.python.org/dev/peps/pep-0484/)
- [typing module documentation](https://docs.python.org/3/library/typing.html)
- SDK Type Conventions: [docs/conventions/types.md](./types.md)
