Files
2025-10-28 12:28:48 -05:00

22 KiB

Skill Examples

This file contains complete, working examples of well-structured skills across different domains.

Example 1: Python Testing Skill

Structure

python-testing-skill/
├── SKILL.md
└── test-templates/
    ├── pytest-basic.py
    └── pytest-advanced.py

SKILL.md

---
name: Python Testing with pytest
description: Create comprehensive Python tests using pytest with fixtures, parametrization, and mocking. Use when writing unit tests, integration tests, or setting up test infrastructure for Python projects.
---

## When to Use This Skill

Use this skill when:
- Writing unit tests for Python functions and classes
- Creating integration tests for Python applications
- Setting up pytest configuration and fixtures
- Implementing test parametrization for multiple scenarios
- Mocking external dependencies in tests

Do NOT use this skill for:
- Testing non-Python code
- End-to-end browser testing (use Playwright skill instead)
- Load testing or performance benchmarking

## Quick Start

Basic test structure:

```python
import pytest
from myapp import add_numbers

def test_add_numbers():
    result = add_numbers(2, 3)
    assert result == 5

Run tests:

pytest tests/ -v

Core Workflows

Workflow 1: Writing Unit Tests

  1. Create a test file matching your source file: test_module.py for module.py
  2. Import the code to test and pytest
  3. Write test functions starting with test_
  4. Use assert statements for validation
  5. Run pytest from project root

Example:

# test_calculator.py
import pytest
from calculator import Calculator

def test_addition():
    calc = Calculator()
    assert calc.add(2, 3) == 5
    assert calc.add(-1, 1) == 0
    assert calc.add(0, 0) == 0

def test_division_by_zero():
    calc = Calculator()
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        calc.divide(10, 0)

Workflow 2: Using Fixtures

  1. Create fixtures for common test setup
  2. Use @pytest.fixture decorator
  3. Accept fixtures as test function parameters
  4. Fixtures run automatically before tests

Example:

import pytest
from database import Database

@pytest.fixture
def db():
    """Provide a test database instance"""
    database = Database(":memory:")
    database.setup()
    yield database
    database.teardown()

def test_create_user(db):
    user = db.create_user("alice", "alice@example.com")
    assert user.name == "alice"
    assert user.email == "alice@example.com"

def test_query_user(db):
    db.create_user("bob", "bob@example.com")
    user = db.query_user("bob")
    assert user is not None

Workflow 3: Parametrized Tests

  1. Use @pytest.mark.parametrize decorator
  2. Provide parameter names and test cases
  3. Test function runs once per parameter set

Example:

import pytest
from validators import validate_email

@pytest.mark.parametrize("email,expected", [
    ("user@example.com", True),
    ("user@domain.co.uk", True),
    ("invalid@", False),
    ("@example.com", False),
    ("no-at-sign.com", False),
    ("", False),
])
def test_email_validation(email, expected):
    assert validate_email(email) == expected

Workflow 4: Mocking External Dependencies

  1. Import unittest.mock or use pytest-mock
  2. Use mocker.patch() to replace dependencies
  3. Configure mock return values or side effects
  4. Verify mock interactions

Example:

import pytest
from unittest.mock import Mock
from weather_app import get_weather

def test_get_weather(mocker):
    # Mock the external API call
    mock_api = mocker.patch('weather_app.weather_api.fetch')
    mock_api.return_value = {
        "temperature": 72,
        "conditions": "sunny"
    }

    result = get_weather("San Francisco")

    assert result["temperature"] == 72
    assert result["conditions"] == "sunny"
    mock_api.assert_called_once_with("San Francisco")

Test Organization

Project Structure

myproject/
├── src/
│   └── myapp/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   ├── conftest.py          # Shared fixtures
│   ├── test_core.py
│   └── test_utils.py
└── pyproject.toml            # pytest configuration

conftest.py for Shared Fixtures

import pytest
from myapp import create_app

@pytest.fixture(scope="session")
def app():
    """Create application for testing"""
    app = create_app(testing=True)
    return app

@pytest.fixture
def client(app):
    """Test client for making requests"""
    return app.test_client()

Configuration

pyproject.toml

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = [
    "-v",
    "--strict-markers",
    "--cov=myapp",
    "--cov-report=term-missing",
]
markers = [
    "slow: marks tests as slow",
    "integration: marks tests as integration tests",
]

Coverage Goals

Aim for:

  • 80%+ code coverage for business logic
  • 100% coverage for critical paths (auth, payments, data integrity)
  • Edge cases and error conditions tested
  • Integration tests for key workflows

Check coverage:

pytest --cov=myapp --cov-report=html
open htmlcov/index.html

Common Pitfalls

Issue: Tests Pass Locally but Fail in CI

Solution:

  • Use fixtures to avoid state pollution between tests
  • Don't rely on file system state or specific paths
  • Mock time-dependent functionality
  • Set explicit random seeds for reproducibility

Issue: Slow Test Suite

Solution:

  • Use @pytest.mark.slow for slow tests, run separately
  • Mock external API calls instead of making real requests
  • Use in-memory databases for tests
  • Run fast unit tests before slow integration tests

Issue: Fixtures Not Running

Solution:

  • Ensure fixture name matches parameter name exactly
  • Check fixture scope (function/class/module/session)
  • Verify conftest.py is in correct location

Advanced Techniques

Async Testing

import pytest

@pytest.mark.asyncio
async def test_async_function():
    result = await async_fetch_data()
    assert result["status"] == "success"

Testing Exceptions with Context

def test_invalid_input():
    with pytest.raises(ValueError) as exc_info:
        process_data(invalid_input)
    assert "expected format" in str(exc_info.value)

Temporary File Testing

def test_file_processing(tmp_path):
    # tmp_path is a pytest fixture providing a temporary directory
    test_file = tmp_path / "test.txt"
    test_file.write_text("test content")

    result = process_file(test_file)
    assert result.success

---

## Example 2: API Integration Skill

### SKILL.md
```markdown
---
name: REST API Integration
description: Design and implement RESTful API integrations with proper error handling, authentication, and rate limiting. Use when building API clients or integrating third-party services.
---

## When to Use This Skill

Use this skill when:
- Integrating third-party REST APIs into your application
- Building API client libraries
- Implementing API authentication flows (OAuth, JWT, API keys)
- Handling rate limiting and retries
- Creating robust error handling for API calls

Do NOT use this skill for:
- GraphQL APIs (different query paradigm)
- gRPC or WebSocket connections
- Building API servers (use framework-specific skills)

## Quick Start

Basic API client structure:

```python
import requests

class APIClient:
    def __init__(self, base_url, api_key):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        })

    def get(self, endpoint):
        response = self.session.get(f"{self.base_url}{endpoint}")
        response.raise_for_status()
        return response.json()

Core Workflows

Workflow 1: Implementing Authentication

API Key Authentication:

class APIKeyClient:
    def __init__(self, api_key):
        self.headers = {"X-API-Key": api_key}

    def request(self, method, url, **kwargs):
        kwargs.setdefault('headers', {}).update(self.headers)
        return requests.request(method, url, **kwargs)

OAuth 2.0 Flow:

from requests_oauthlib import OAuth2Session

class OAuthClient:
    def __init__(self, client_id, client_secret, redirect_uri):
        self.client_id = client_id
        self.client_secret = client_secret
        self.redirect_uri = redirect_uri
        self.session = None

    def get_authorization_url(self, authorization_base_url):
        oauth = OAuth2Session(self.client_id, redirect_uri=self.redirect_uri)
        authorization_url, state = oauth.authorization_url(authorization_base_url)
        return authorization_url, state

    def fetch_token(self, token_url, authorization_response):
        oauth = OAuth2Session(self.client_id, redirect_uri=self.redirect_uri)
        token = oauth.fetch_token(
            token_url,
            authorization_response=authorization_response,
            client_secret=self.client_secret
        )
        self.session = oauth
        return token

Workflow 2: Error Handling and Retries

import time
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

class RobustAPIClient:
    def __init__(self, base_url, api_key, max_retries=3):
        self.base_url = base_url
        self.session = requests.Session()

        # Configure retry strategy
        retry_strategy = Retry(
            total=max_retries,
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["GET", "POST", "PUT", "DELETE"]
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("http://", adapter)
        self.session.mount("https://", adapter)

        self.session.headers.update({
            "Authorization": f"Bearer {api_key}"
        })

    def request(self, method, endpoint, **kwargs):
        url = f"{self.base_url}{endpoint}"
        try:
            response = self.session.request(method, url, **kwargs)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 401:
                raise AuthenticationError("Invalid or expired API key")
            elif e.response.status_code == 404:
                raise NotFoundError(f"Resource not found: {endpoint}")
            elif e.response.status_code == 429:
                raise RateLimitError("Rate limit exceeded")
            else:
                raise APIError(f"API request failed: {e}")
        except requests.exceptions.RequestException as e:
            raise APIError(f"Network error: {e}")

class APIError(Exception): pass
class AuthenticationError(APIError): pass
class NotFoundError(APIError): pass
class RateLimitError(APIError): pass

Workflow 3: Rate Limiting

import time
from threading import Lock

class RateLimiter:
    def __init__(self, calls_per_second):
        self.calls_per_second = calls_per_second
        self.min_interval = 1.0 / calls_per_second
        self.last_call = 0
        self.lock = Lock()

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            with self.lock:
                elapsed = time.time() - self.last_call
                if elapsed < self.min_interval:
                    time.sleep(self.min_interval - elapsed)
                self.last_call = time.time()
            return func(*args, **kwargs)
        return wrapper

class RateLimitedClient:
    def __init__(self, base_url, api_key, rate_limit=10):
        self.client = APIClient(base_url, api_key)
        self.limiter = RateLimiter(rate_limit)

    @property
    def get(self):
        return self.limiter(self.client.get)

    @property
    def post(self):
        return self.limiter(self.client.post)

Workflow 4: Pagination Handling

class PaginatedAPIClient(APIClient):
    def get_all_pages(self, endpoint, params=None):
        """Fetch all pages of results"""
        results = []
        page = 1
        params = params or {}

        while True:
            params['page'] = page
            response = self.get(endpoint, params=params)

            # Adjust based on API response structure
            items = response.get('items', [])
            results.extend(items)

            # Check if more pages exist
            if not response.get('has_more', False):
                break

            page += 1

        return results

    def get_all_cursor(self, endpoint, params=None):
        """Fetch all results using cursor-based pagination"""
        results = []
        cursor = None
        params = params or {}

        while True:
            if cursor:
                params['cursor'] = cursor

            response = self.get(endpoint, params=params)
            items = response.get('data', [])
            results.extend(items)

            cursor = response.get('next_cursor')
            if not cursor:
                break

        return results

Common Patterns

Request Caching

from functools import lru_cache
import hashlib
import json

class CachedAPIClient(APIClient):
    def __init__(self, base_url, api_key, cache_ttl=300):
        super().__init__(base_url, api_key)
        self.cache = {}
        self.cache_ttl = cache_ttl

    def _cache_key(self, method, endpoint, params):
        key_data = f"{method}:{endpoint}:{json.dumps(params, sort_keys=True)}"
        return hashlib.md5(key_data.encode()).hexdigest()

    def get_cached(self, endpoint, params=None):
        cache_key = self._cache_key("GET", endpoint, params or {})

        if cache_key in self.cache:
            cached_data, cached_time = self.cache[cache_key]
            if time.time() - cached_time < self.cache_ttl:
                return cached_data

        data = self.get(endpoint, params=params)
        self.cache[cache_key] = (data, time.time())
        return data

Webhook Signature Verification

import hmac
import hashlib

class WebhookValidator:
    def __init__(self, secret):
        self.secret = secret.encode()

    def verify_signature(self, payload, signature_header):
        """Verify webhook signature"""
        expected_signature = hmac.new(
            self.secret,
            payload.encode(),
            hashlib.sha256
        ).hexdigest()

        return hmac.compare_digest(expected_signature, signature_header)

Testing API Integrations

Using responses Library

import responses
import requests

@responses.activate
def test_api_get():
    # Mock the API response
    responses.add(
        responses.GET,
        'https://api.example.com/users/123',
        json={'id': 123, 'name': 'Alice'},
        status=200
    )

    client = APIClient('https://api.example.com', 'test-key')
    user = client.get('/users/123')

    assert user['name'] == 'Alice'

@responses.activate
def test_api_error_handling():
    responses.add(
        responses.GET,
        'https://api.example.com/users/999',
        json={'error': 'Not found'},
        status=404
    )

    client = RobustAPIClient('https://api.example.com', 'test-key')

    with pytest.raises(NotFoundError):
        client.request('GET', '/users/999')

Common Pitfalls

Issue: Leaking API Keys

Solution: Never hardcode keys. Use environment variables or secret management:

import os

api_key = os.environ.get('API_KEY')
if not api_key:
    raise ValueError("API_KEY environment variable not set")

Issue: Not Handling Rate Limits

Solution: Implement exponential backoff and respect rate limit headers:

def handle_rate_limit(response):
    if response.status_code == 429:
        retry_after = int(response.headers.get('Retry-After', 60))
        time.sleep(retry_after)
        # Retry request

Issue: Timeout Issues

Solution: Always set timeouts:

response = self.session.get(url, timeout=(3.0, 10.0))  # (connect, read)

---

## Example 3: Documentation Skill

### SKILL.md
```markdown
---
name: Technical Documentation Writer
description: Create clear, comprehensive technical documentation including API docs, README files, user guides, and code comments. Use when documenting code, APIs, or writing user-facing technical content.
---

## When to Use This Skill

Use this skill when:
- Writing README.md files for repositories
- Documenting APIs (REST, GraphQL, SDKs)
- Creating user guides and tutorials
- Writing inline code documentation
- Building developer onboarding materials

Do NOT use this skill for:
- Marketing copy or sales content
- Academic papers or research documentation
- Legal or compliance documentation

## Core Principles

1. **Write for your audience**: Junior developers need more context than senior engineers
2. **Show, don't just tell**: Include code examples for every concept
3. **Start with "why"**: Explain the purpose before the implementation
4. **Maintain consistency**: Use the same terminology throughout
5. **Keep it current**: Documentation that's out of date is worse than no documentation

## README.md Template

```markdown
# Project Name

Brief (1-2 sentence) description of what this project does.

## Features

- Key feature 1
- Key feature 2
- Key feature 3

## Installation

\```bash
npm install project-name
\```

## Quick Start

\```javascript
const Project = require('project-name');

const instance = new Project({
  apiKey: 'your-api-key'
});

const result = await instance.doSomething();
console.log(result);
\```

## Documentation

Full documentation available at [link]

## Configuration

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | required | Your API key |
| timeout | number | 5000 | Request timeout in ms |

## Examples

### Example 1: Basic Usage
[Code example with explanation]

### Example 2: Advanced Pattern
[Code example with explanation]

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md)

## License

[License type] - see [LICENSE](LICENSE)

API Documentation Template

## endpoint_name

Brief description of what this endpoint does.

### HTTP Request

\```
POST /api/v1/resource
\```

### Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| name | string | Yes | Resource name |
| type | string | No | Resource type (default: "standard") |
| metadata | object | No | Additional metadata |

### Request Example

\```json
{
  "name": "example-resource",
  "type": "premium",
  "metadata": {
    "created_by": "user123"
  }
}
\```

### Response

\```json
{
  "id": "res_abc123",
  "name": "example-resource",
  "type": "premium",
  "status": "active",
  "created_at": "2024-01-15T10:30:00Z"
}
\```

### Errors

| Status Code | Error Code | Description |
|-------------|------------|-------------|
| 400 | invalid_name | Name contains invalid characters |
| 401 | unauthorized | Invalid or missing API key |
| 409 | duplicate | Resource with this name already exists |

### Example Usage

\```python
import requests

response = requests.post(
    'https://api.example.com/api/v1/resource',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
    json={
        'name': 'example-resource',
        'type': 'premium'
    }
)

resource = response.json()
print(f"Created resource: {resource['id']}")
\```

Code Comment Guidelines

Function Documentation

Python (docstring):

def calculate_discount(price: float, discount_percent: float, min_price: float = 0) -> float:
    """
    Calculate discounted price with minimum price floor.

    Args:
        price: Original price before discount
        discount_percent: Discount percentage (0-100)
        min_price: Minimum price floor (default: 0)

    Returns:
        Final price after applying discount, never below min_price

    Raises:
        ValueError: If discount_percent is not between 0 and 100
        ValueError: If price or min_price is negative

    Examples:
        >>> calculate_discount(100, 20)
        80.0
        >>> calculate_discount(100, 20, min_price=85)
        85.0
    """
    if not 0 <= discount_percent <= 100:
        raise ValueError("Discount percent must be between 0 and 100")
    if price < 0 or min_price < 0:
        raise ValueError("Prices cannot be negative")

    discounted = price * (1 - discount_percent / 100)
    return max(discounted, min_price)

JavaScript (JSDoc):

/**
 * Fetch user data from the API with caching
 *
 * @param {string} userId - The unique user identifier
 * @param {Object} options - Configuration options
 * @param {boolean} [options.skipCache=false] - Whether to bypass cache
 * @param {number} [options.timeout=5000] - Request timeout in ms
 * @returns {Promise<User>} The user object
 * @throws {NotFoundError} If user doesn't exist
 * @throws {APIError} If the API request fails
 *
 * @example
 * const user = await fetchUser('user123');
 * console.log(user.name);
 *
 * @example
 * // Skip cache for fresh data
 * const user = await fetchUser('user123', { skipCache: true });
 */
async function fetchUser(userId, options = {}) {
  // Implementation
}

Inline Comments

Good inline comments explain "why", not "what":

# Good: Explains reasoning
# Use a Set for O(1) lookup time since we'll be checking membership frequently
seen_ids = set()

# Bad: Just repeats what the code says
# Create a set called seen_ids
seen_ids = set()
# Good: Explains non-obvious behavior
# Delay between requests to avoid hitting rate limit (10 req/sec)
time.sleep(0.1)

# Bad: States the obvious
# Sleep for 0.1 seconds
time.sleep(0.1)

Common Pitfalls

Issue: Documentation Out of Sync with Code

Solution:

  • Keep docs close to code (docstrings, inline comments)
  • Auto-generate API docs from code when possible
  • Include doc updates in pull request checklist
  • Set up CI checks for documentation completeness

Issue: Too Much or Too Little Detail

Solution:

  • README: High-level overview + quick start
  • API Docs: Complete reference for every parameter
  • Tutorials: Step-by-step with context
  • Code Comments: Why, not what

Issue: Examples Don't Run

Solution:

  • Test all code examples as part of CI
  • Use tools like doctest (Python) or jsdoc-to-markdown
  • Include a "examples" directory with runnable code
  • Version examples alongside code releases