agentleFS
jarosser06jarosser06/drift/.claude/skills/python-basics/SKILL.md

python-basics

Skill1 starsChanged 11 months agoMITView on GitHub

Expert in Python 3.10+ fundamentals and best practices for the Drift project including import ordering (isort), code organization, and PEP 8 conventions. Use when writing or reviewing Python code.

The file

Show its rules and text

We split this skill into the rules it gives an agent

14 rules. About 0 are common, 13 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈13

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    When writing Python files, place imports immediately after the module docstring and organize them in three groups:

    line 12
  2. Distinctive:

    When you need to avoid circular imports for type hints only:

    line 67
  3. Distinctive:

    When your linter flags import problems:

    line 84
  4. Distinctive:

    Follow this organization pattern:

    line 142
  5. Distinctive:

    When you encounter circular import errors, try these solutions in order:

    line 181
  6. Distinctive:

    Only when the above solutions don't work:

    line 208
  7. Distinctive:

    Use this pattern for function signatures:

    line 220
  8. Distinctive:

    When to catch broad exceptions:

    line 275
  9. Distinctive:

    When you need to clean up resources regardless of error type

    line 277
  10. Distinctive:

    When logging all errors for debugging

    line 278
  11. Distinctive:

    Use f-strings for readability and performance:

    line 282
  12. Distinctive:

    When reviewing or writing Python code, work through these steps:

    line 312
  13. Distinctive:

    Do public functions have type hints?

    line 321
  14. Very specific:

    Run isort and your linter will catch these automatically.

    line 389

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Python Basics Skill

Learn how to write clean, well-organized Python code following Drift project conventions.

How to Organize Imports

When writing Python files, place imports immediately after the module docstring and organize them in three groups:

"""Module for analyzing conversation logs."""

# Standard library imports
import json
import os
from pathlib import Path
from typing import Any, Optional

# Third-party imports
import boto3
import click
from botocore.exceptions import ClientError

# Local imports
from drift.core.parser import parse_conversation
from drift.core.detector import DriftDetector
from drift.utils.config import load_config

Within Each Group

Organize imports consistently:

  1. import x statements first
  2. from x import y statements second
  3. Alphabetically within each subgroup
# Good organization
import json
import os
from pathlib import Path
from typing import Any, Dict

# Poor organization (mixed order)
from typing import Any, Dict
import os
from pathlib import Path
import json

Why No Inline Imports?

Understanding the reasoning helps you make better architectural decisions:

  1. Readability: Dependencies should be visible at the top of the file
  2. Maintainability: Easier to track and manage dependencies
  3. Performance: Module loading happens once, not repeatedly
  4. Static Analysis: Tools can analyze dependencies properly
  5. PEP 8 Compliance: Follows Python style guide recommendations

The Exception: TYPE_CHECKING Blocks

When you need to avoid circular imports for type hints only:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from drift.core.detector import DriftDetector  # OK: Type checking only

def process(detector: "DriftDetector") -> None:
    """Process using detector."""
    pass

This technique allows forward references without runtime circular dependencies.

How to Fix Import Issues

When your linter flags import problems:

Using isort

# Auto-sort all imports
isort .

# Check what would change
isort --check-only .

# Sort a specific file
isort src/drift/core/parser.py

Manual Fixes

Problem: Imports mixed with code

# Before
import json

def process():
    pass

import os  # Wrong location!
# After
import json
import os

def process():
    pass

Problem: Wrong grouping

# Before
import json
import click  # Third-party mixed with stdlib
from drift.core import parser
# After
import json

import click

from drift.core import parser

How to Structure Python Modules

Follow this organization pattern:

"""Module for drift detection.

This module provides the core drift detection functionality.
"""

# Imports (organized as shown above)
import json
from typing import Any

from drift.core.parser import parse_conversation

# Constants
DEFAULT_MODEL = "anthropic.claude-v2"
MAX_RETRIES = 3

# Classes
class DriftDetector:
    """Detects drift patterns in conversations."""

    def __init__(self, model: str = DEFAULT_MODEL):
        """Initialize detector."""
        self.model = model

# Functions
def detect_drift(conversation: dict) -> list[str]:
    """Detect drift in conversation."""
    detector = DriftDetector()
    return detector.analyze(conversation)

# Main execution
if __name__ == "__main__":
    main()

How to Handle Circular Imports

When you encounter circular import errors, try these solutions in order:

1. Refactor to Remove the Circle (Preferred)

Move shared code to a separate module:

# Before: module_a.py imports module_b.py, module_b.py imports module_a.py

# After: Create module_c.py with shared code
# module_a.py imports module_c.py
# module_b.py imports module_c.py

2. Use Dependency Injection

Pass dependencies as function parameters instead of importing:

# Instead of importing DriftDetector in multiple places
def analyze_file(path: str, detector: DriftDetector) -> dict:
    """Analyze file using provided detector."""
    return detector.analyze(path)

3. Import at Function Level (Last Resort)

Only when the above solutions don't work:

def process_with_detector():
    """Process using detector."""
    from drift.core.detector import DriftDetector  # Local import
    detector = DriftDetector()
    return detector.analyze()

How to Add Type Hints

Use this pattern for function signatures:

from typing import Optional

def detect_drift(
    conversation: dict,
    drift_type: str,
    config: Optional[dict] = None
) -> list[str]:
    """Detect drift in conversation.

    -- conversation: Conversation data
    -- drift_type: Type of drift to detect
    -- config: Optional configuration

    Returns list of detected drift patterns.
    """
    pass

For optional parameters:

from typing import Optional

def fetch_user(user_id: str, cache: Optional[Cache] = None) -> User:
    """Fetch user by ID with optional caching."""
    if cache:
        return cache.get(user_id)
    return database.fetch(user_id)

How to Handle Errors Effectively

Be specific with exception types:

# Good - specific exceptions
try:
    data = json.load(f)
except json.JSONDecodeError as e:
    logger.error(f"Invalid JSON in {path}: {e}")
    raise ValueError(f"Cannot parse {path}") from e
except FileNotFoundError:
    logger.error(f"File not found: {path}")
    raise

# Avoid - too broad
try:
    data = json.load(f)
except Exception as e:  # Catches too much!
    raise

When to catch broad exceptions:

  • At application boundaries (CLI entry points)
  • When you need to clean up resources regardless of error type
  • When logging all errors for debugging

How to Format Strings

Use f-strings for readability and performance:

# Recommended
message = f"Found {count} drift instances in {file_path}"
details = f"Status: {status}, Time: {elapsed:.2f}s"

# Avoid (harder to read)
message = "Found {} drift instances in {}".format(count, file_path)
message = "Found " + str(count) + " drift instances"

How to Use Black for Formatting

# Format all files
black .

# Check what would change without modifying
black --check .

# Format specific file
black src/drift/core/parser.py

# See what would change
black --diff src/drift/core/parser.py

Code Review Workflow

When reviewing or writing Python code, work through these steps:

1. Check Imports

  • Are all imports at the top?
  • Are they grouped correctly (stdlib → third-party → local)?
  • Any inline imports that should be moved?
  • Any wildcard or unused imports?

2. Check Type Hints

  • Do public functions have type hints?
  • Are Optional types used correctly?

3. Check Naming

  • Functions/variables: snake_case?
  • Classes: PascalCase?
  • Constants: UPPER_CASE?
  • Private identifiers: _leading_underscore?

4. Check Formatting

  • Lines under 100 characters?
  • Proper spacing (2 lines between classes/functions, 1 line between methods)?
  • No trailing whitespace?

5. Run Auto-formatters

# Fix all formatting automatically
black .
isort .

# Verify
black --check .
isort --check-only .

Anti-Pattern Examples

Inline Imports

# Avoid this pattern
def process_data(data):
    import json  # Import should be at top
    return json.loads(data)

# Use this instead
import json

def process_data(data):
    return json.loads(data)

Wildcard Imports

# Avoid - unclear what's being imported
from drift.core import *

# Use explicit imports
from drift.core import DriftDetector, parse_conversation

Unused Imports

# Avoid - these imports aren't used
import json
import os
from typing import Any

def hello():
    print("hello")

# Keep it clean
def hello():
    print("hello")

Run isort and your linter will catch these automatically.

Common Patterns

Optional Configuration

from typing import Optional

def analyze(
    data: dict,
    config: Optional[dict] = None
) -> dict:
    """Analyze data with optional configuration."""
    if config is None:
        config = get_default_config()
    return _analyze_impl(data, config)

Path Handling

from pathlib import Path

def read_config(config_path: str | Path) -> dict:
    """Read configuration from file."""
    path = Path(config_path)
    if not path.exists():
        raise FileNotFoundError(f"Config not found: {path}")
    return json.loads(path.read_text())

Context Managers

def process_file(path: str) -> dict:
    """Process file with automatic cleanup."""
    with open(path) as f:
        data = json.load(f)
    # File automatically closed here
    return data

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.