# Python Integration

Spyglasses integrates seamlessly with Python web applications through our official Python SDK. The SDK provides middleware that works with Django, Flask, FastAPI, and any other WSGI/ASGI-based Python framework.

## Installation

1. **Install the Python package**:
   ```bash
   pip install spyglasses
   ```

   Or with framework-specific dependencies:
   ```bash
   # For Django projects
   pip install 'spyglasses[django]'

   # For Flask projects  
   pip install 'spyglasses[flask]'

   # For FastAPI projects
   pip install 'spyglasses[fastapi]'
   ```

2. **Set your API key**:
   Add your Spyglasses API key to your environment variables:
   ```env
   SPYGLASSES_API_KEY=your_api_key_here
   ```

## Django Integration

### Option 1: Simple Middleware Setup (Recommended)

Add the middleware to your Django application in `settings.py`:

```python
# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'spyglasses.middleware.DjangoMiddleware',  # Add here
    'django.contrib.sessions.middleware.SessionMiddleware',
    # ... your other middleware
]

# Optional: Configure Spyglasses settings
SPYGLASSES_API_KEY = os.getenv('SPYGLASSES_API_KEY')
SPYGLASSES_DEBUG = True  # Enable debug logging
```

### Option 2: Advanced Configuration

Create a configuration for more control:

```python
# settings.py

# Spyglasses Configuration
SPYGLASSES_API_KEY = os.getenv('SPYGLASSES_API_KEY')
SPYGLASSES_DEBUG = DEBUG  # Use Django's DEBUG setting
SPYGLASSES_PLATFORM_TYPE = 'django'
SPYGLASSES_EXCLUDE_PATHS = [
    '/admin/',               # Exclude Django admin
    '/static/',              # Exclude static files
    '/media/',               # Exclude media files
    '/api/internal/',        # Exclude internal API routes
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'spyglasses.middleware.DjangoMiddleware',
    # ... other middleware
]
```

### Programmatic Configuration

For more advanced setups, configure programmatically:

```python
# apps.py or __init__.py
from django.apps import AppConfig
from spyglasses import configure, Configuration

class MyAppConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'myapp'
    
    def ready(self):
        configure(Configuration(
            api_key=os.getenv('SPYGLASSES_API_KEY'),
            debug=settings.DEBUG,
            platform_type='django',
            exclude_paths=['/admin/', '/api/internal/']
        ))
```

### Django-Specific Considerations

**Static Files**: Django static files (`/static/`, `/media/`) are automatically excluded from monitoring.

**Admin Interface**: Django admin routes (`/admin/`) are automatically excluded by default.

**API Applications**: For Django REST Framework or API-only applications:

```python
# settings.py
REST_FRAMEWORK = {
    # Your DRF settings
}

# Spyglasses works normally with DRF
MIDDLEWARE = [
    'spyglasses.middleware.DjangoMiddleware',
    # ... other middleware including DRF
]
```

**Development vs Production**: Consider different settings per environment:

```python
# settings.py
if DEBUG:
    SPYGLASSES_DEBUG = True
    SPYGLASSES_EXCLUDE_PATHS = ['/debug-toolbar/']
else:
    SPYGLASSES_DEBUG = False
    SPYGLASSES_AUTO_SYNC = True
```

## Flask Integration

### Basic Flask Setup

```python
# app.py
from flask import Flask
from spyglasses.middleware import FlaskMiddleware

app = Flask(__name__)

# Initialize Spyglasses middleware
middleware = FlaskMiddleware(
    app=app,
    api_key=os.getenv('SPYGLASSES_API_KEY'),
    debug=app.debug,
    platform_type='flask'
)

@app.route('/')
def hello():
    return 'Hello World!'
```

### Flask with Configuration

```python
# app.py
from flask import Flask
from spyglasses import Configuration
from spyglasses.middleware import FlaskMiddleware

app = Flask(__name__)

# Create Spyglasses configuration
config = Configuration(
    api_key=os.getenv('SPYGLASSES_API_KEY'),
    debug=app.debug,
    platform_type='flask',
    exclude_paths=['/health', '/metrics']
)

# Initialize middleware with configuration
middleware = FlaskMiddleware(app=app, configuration=config)
```

### Route-Specific Protection

Use the decorator for protecting specific routes:

```python
from spyglasses.middleware.flask import spyglasses_middleware

@app.route('/api/sensitive')
@spyglasses_middleware(api_key=os.getenv('SPYGLASSES_API_KEY'))
def sensitive_data():
    return {'data': 'sensitive information'}
```

### Flask Blueprints

Spyglasses works automatically with Flask blueprints:

```python
# blueprints.py
from flask import Blueprint

api = Blueprint('api', __name__, url_prefix='/api')

@api.route('/data')
def get_data():
    return {'data': 'protected by Spyglasses'}

# Register blueprint
app.register_blueprint(api)
```

## FastAPI Integration

### Basic FastAPI Setup

```python
# main.py
from fastapi import FastAPI
from spyglasses.middleware import FastAPIMiddleware

app = FastAPI()

# Add Spyglasses middleware
app.add_middleware(
    FastAPIMiddleware,
    api_key=os.getenv('SPYGLASSES_API_KEY'),
    debug=True,
    platform_type='fastapi'
)

@app.get("/")
async def root():
    return {"message": "Hello World"}
```

### FastAPI with Advanced Configuration

```python
# main.py
from fastapi import FastAPI
from spyglasses import Configuration
from spyglasses.middleware import FastAPIMiddleware

app = FastAPI()

# Create configuration
config = Configuration(
    api_key=os.getenv('SPYGLASSES_API_KEY'),
    debug=True,
    platform_type='fastapi',
    exclude_paths=['/docs', '/redoc', '/openapi.json']
)

# Add middleware with configuration
app.add_middleware(FastAPIMiddleware, configuration=config)
```

### Convenience Setup Function

```python
# main.py
from fastapi import FastAPI
from spyglasses.middleware.fastapi import setup_spyglasses

app = FastAPI()

# Easy setup with all defaults
setup_spyglasses(
    app,
    api_key=os.getenv('SPYGLASSES_API_KEY'),
    debug=True
)
```

## Other Python Frameworks

### Starlette

```python
# app.py
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from spyglasses.middleware import ASGIMiddleware

async def homepage(request):
    return JSONResponse({'hello': 'world'})

app = Starlette(routes=[
    Route('/', homepage),
])

# Wrap with Spyglasses
app = ASGIMiddleware(
    app,
    api_key=os.getenv('SPYGLASSES_API_KEY'),
    platform_type='starlette'
)
```

### Tornado

```python
# app.py

from spyglasses.middleware import WSGIMiddleware

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

app = tornado.web.Application([
    (r"/", MainHandler),
])

# For WSGI-compatible setup
from tornado.wsgi import WSGIAdapter
wsgi_app = WSGIAdapter(app)
wsgi_app = WSGIMiddleware(
    wsgi_app,
    api_key=os.getenv('SPYGLASSES_API_KEY')
)
```

### Pyramid

```python
# app.py
from pyramid.config import Configurator
from pyramid.response import Response
from spyglasses.middleware import WSGIMiddleware

def hello_world(request):
    return Response('Hello World!')

if __name__ == '__main__':
    with Configurator() as config:
        config.add_route('hello', '/')
        config.add_view(hello_world, route_name='hello')
        app = config.make_wsgi_app()
    
    # Wrap with Spyglasses
    app = WSGIMiddleware(
        app,
        api_key=os.getenv('SPYGLASSES_API_KEY'),
        platform_type='pyramid'
    )
```

### Pure WSGI/ASGI

For any WSGI application:

```python
# wsgi_app.py
from spyglasses.middleware import WSGIMiddleware

def application(environ, start_response):
    status = '200 OK'
    headers = [('Content-type', 'text/plain')]
    start_response(status, headers)
    return [b'Hello World!']

# Wrap with Spyglasses
application = WSGIMiddleware(
    application,
    api_key=os.getenv('SPYGLASSES_API_KEY')
)
```

For any ASGI application:

```python
# asgi_app.py
from spyglasses.middleware import ASGIMiddleware

async def application(scope, receive, send):
    assert scope['type'] == 'http'
    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [[b'content-type', b'text/plain']],
    })
    await send({
        'type': 'http.response.body',
        'body': b'Hello World!',
    })

# Wrap with Spyglasses
application = ASGIMiddleware(
    application,
    api_key=os.getenv('SPYGLASSES_API_KEY')
)
```

## Configuration Options

All middleware classes accept these configuration options:

```python
middleware = FrameworkMiddleware(
    api_key=os.getenv('SPYGLASSES_API_KEY'),   # Required: Your API key
    debug=False,                               # Enable debug logging
    auto_sync=True,                            # Auto-sync patterns on startup
    platform_type='python',                    # Platform identifier
    cache_ttl=86400,                           # Pattern cache TTL (24 hours)
    exclude_paths=[                            # Paths to exclude
        '/admin/',                             # String matching
        '/api/internal/',                      # Prefix matching
        '/health'                              # Exact matching
    ],
    exclude_extensions=[                       # File extensions to exclude
        '.ico', '.png', '.css', '.js'
    ],
    collect_endpoint='https://...',            # Custom collector endpoint
    patterns_endpoint='https://...'            # Custom patterns endpoint
)
```

## Environment Variables

Configure Spyglasses using environment variables:

| Variable | Description | Default |
|----------|-------------|---------|
| `SPYGLASSES_API_KEY` | Your Spyglasses API key | Required |
| `SPYGLASSES_DEBUG` | Enable debug logging | `false` |
| `SPYGLASSES_AUTO_SYNC` | Auto-sync patterns on startup | `true` |
| `SPYGLASSES_CACHE_TTL` | Pattern cache TTL in seconds | `86400` (24 hours) |
| `SPYGLASSES_COLLECT_ENDPOINT` | Custom collector endpoint | `https://www.spyglasses.io/api/collect` |
| `SPYGLASSES_PATTERNS_ENDPOINT` | Custom patterns endpoint | `https://www.spyglasses.io/api/patterns` |
| `SPYGLASSES_PLATFORM_TYPE` | Platform identifier | `python` |

## Deployment

### Docker

Add environment variables to your Dockerfile:

```dockerfile
# Dockerfile
FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

ENV SPYGLASSES_API_KEY=your_api_key_here
ENV SPYGLASSES_DEBUG=false

COPY . .
CMD ["python", "app.py"]
```

Or use docker-compose:

```yaml
# docker-compose.yml
version: '3.8'
services:
  web:
    build: .
    environment:
      - SPYGLASSES_API_KEY=your_api_key_here
      - SPYGLASSES_DEBUG=false
    ports:
      - "8000:8000"
```

### Heroku

Add your API key to Heroku config vars:

```bash
heroku config:set SPYGLASSES_API_KEY=your_api_key_here
```

### AWS Lambda

For serverless applications using frameworks like Chalice or Zappa:

```python
# app.py

from chalice import Chalice
from spyglasses import Client

app = Chalice(app_name='myapp')

# Initialize Spyglasses client for manual usage
client = Client(api_key=os.getenv('SPYGLASSES_API_KEY'))

@app.route('/')
def index():
    # Manual detection in serverless environment
    user_agent = app.current_request.headers.get('user-agent', '')
    result = client.detect(user_agent)
    
    if result.is_bot:
        # Handle bot detection
        pass
    
    return {'hello': 'world'}
```

### Google Cloud Platform

Set environment variables in your app.yaml:

```yaml
# app.yaml
runtime: python39

env_variables:
  SPYGLASSES_API_KEY: "your_api_key_here"
  SPYGLASSES_DEBUG: "false"
```

### Railway

Add environment variables in your Railway dashboard or railway.json:

```json
{
  "$schema": "https://railway.app/railway.schema.json",
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "envVars": {
      "SPYGLASSES_API_KEY": "your_api_key_here"
    }
  }
}
```

## Performance Considerations

The Spyglasses Python SDK is optimized for high-performance applications:

- **Minimal Overhead**: Typically under 1ms per request
- **Background Logging**: API calls run in background threads
- **Smart Exclusions**: Static assets automatically excluded
- **Pattern Caching**: Compiled regex patterns are cached
- **Thread Safety**: All operations are thread-safe

### ASGI vs WSGI Performance

- **ASGI** (FastAPI, Starlette): Best for async applications
- **WSGI** (Django, Flask): Traditional sync applications
- Both implementations are optimized for their respective paradigms

### Async Compatibility

The SDK works seamlessly with async frameworks:

```python
# FastAPI example with async views
from fastapi import FastAPI
from spyglasses.middleware import FastAPIMiddleware

app = FastAPI()
app.add_middleware(FastAPIMiddleware, api_key="your-key")

@app.get("/")
async def async_endpoint():
    # Spyglasses detection happens automatically
    return {"message": "async response"}
```

## Testing

### Disable in Test Environment

```python
# conftest.py (pytest) or test_settings.py (Django)

# Disable Spyglasses in tests
os.environ['SPYGLASSES_API_KEY'] = ''
os.environ['SPYGLASSES_AUTO_SYNC'] = 'false'
```

### Mock API Calls

For integration tests, mock the API endpoints:

```python
# test_spyglasses.py

from unittest.mock import patch
from spyglasses import Client

class TestSpyglasses:
    @patch('spyglasses.client.requests.get')
    @patch('spyglasses.client.requests.post')
    def test_bot_detection(self, mock_post, mock_get):
        # Mock API responses
        mock_get.return_value.status_code = 200
        mock_get.return_value.json.return_value = {
            'patterns': [],
            'ai_referrers': [],
            'version': '1.0.0'
        }
        
        client = Client(api_key='test-key')
        result = client.detect('GPTBot/1.0')
        
        assert result.is_bot
```

### Django Test Setup

```python
# test_settings.py
from .settings import *

# Disable Spyglasses for tests
SPYGLASSES_API_KEY = None
SPYGLASSES_AUTO_SYNC = False

# Remove middleware for faster tests
MIDDLEWARE = [m for m in MIDDLEWARE if 'spyglasses' not in m.lower()]
```

### pytest Configuration

```python
# conftest.py

from spyglasses import reset_configuration

@pytest.fixture(autouse=True)
def reset_spyglasses():
    """Reset Spyglasses configuration before each test."""
    reset_configuration()
    yield
    reset_configuration()
```

## Manual Usage

You can use Spyglasses without middleware for custom implementations:

```python
from spyglasses import Client, Configuration

# Initialize client
config = Configuration(api_key='your-api-key')
client = Client(config)

# Detect bots
user_agent = 'Mozilla/5.0 (compatible; ChatGPT-User/1.0)'
result = client.detect_bot(user_agent)

if result.is_bot:
    print(f"Bot detected: {result.info.company}")
    print(f"Should block: {result.should_block}")

# Detect AI referrers
referrer = 'https://chat.openai.com/'
result = client.detect_ai_referrer(referrer)

if result.source_type == 'ai_referrer':
    print(f"AI referrer: {result.info.name}")

# Combined detection
result = client.detect(user_agent, referrer)

# Manual logging
request_info = {
    'url': 'https://example.com/api',
    'user_agent': user_agent,
    'ip_address': '192.168.1.1',
    'request_method': 'GET',
    'request_path': '/api',
    'request_query': 'param=value',
    'referrer': referrer,
    'response_status': 200,
    'response_time_ms': 150,
    'headers': {'Host': 'example.com'}
}

client.log_request(result, request_info)
```

## Verifying Installation

After deploying your Python application with Spyglasses:

1. **Check logs** for Spyglasses initialization (if debug enabled)
2. **Test with curl** to generate bot traffic:
   ```bash
   curl -H "User-Agent: GPTBot/1.0" https://yoursite.com/
   ```
3. **Monitor your dashboard** at [spyglasses.io](https://www.spyglasses.io) for incoming data
4. **Enable debug temporarily** to see detection in action:
   ```python
   os.environ['SPYGLASSES_DEBUG'] = 'true'
   ```

## Troubleshooting

### Common Issues

**Package not found**
- Ensure you've installed with `pip install spyglasses`
- Check virtual environment activation
- Verify Python version compatibility (3.8+)

**API key not found**
- Verify `SPYGLASSES_API_KEY` environment variable is set
- Check for typos in environment variable name
- Ensure deployment includes environment variables

**Middleware not working**
- Verify middleware is added to your framework correctly
- Check middleware order (should be early in the stack)
- Enable debug mode to see processing logs

**Import errors**
- Install framework-specific extras: `pip install 'spyglasses[django]'`
- Check for conflicting package versions
- Ensure all dependencies are installed

**Performance issues**
- Review exclude_paths for overly broad patterns
- Monitor background thread performance
- Check network latency to Spyglasses endpoints

### Debug Mode

Enable debug logging to troubleshoot:

```python

os.environ['SPYGLASSES_DEBUG'] = 'true'

# Or programmatically
from spyglasses import configure, Configuration
configure(Configuration(debug=True))
```

Debug output includes:
- Pattern loading and synchronization
- Request processing and detection results
- API communication and responses
- Performance timing information

### Framework-Specific Issues

**Django**
- Ensure middleware is in correct position in MIDDLEWARE list
- Check for conflicts with authentication middleware
- Verify Django version compatibility (3.2+ recommended)

**Flask**
- Confirm Flask version compatibility (2.0+ recommended)
- Check for blueprint registration order
- Verify WSGI server configuration

**FastAPI**
- Ensure FastAPI version compatibility (0.68+ recommended)
- Check middleware order with other ASGI middleware
- Verify async/await usage is correct

### Memory and Threading

Monitor resource usage:
- Check thread count for background logging
- Monitor memory usage with pattern caching
- Adjust cache_ttl if patterns update frequently

Need help? Contact support@spyglasses.io
