Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:30:04 +08:00
commit 833eac73ea
20 changed files with 2939 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# Python Code Quality Skill
Ensure Python code follows best practices with automated linting, formatting, and type checking using ruff and mypy.
## When to Use This Skill
Use this skill when:
- User requests code linting or formatting
- User mentions code quality, PEP 8, or style issues
- User wants to fix linting errors
- User asks about type checking
- Code review processes require quality checks
## Core Capabilities
1. **Linting with Ruff**
- Check code for style violations
- Identify potential bugs and code smells
- Enforce PEP 8 compliance
- Auto-fix issues where possible
2. **Code Formatting**
- Format code consistently with ruff
- Organize imports automatically
- Ensure consistent indentation and line length
3. **Type Checking with Mypy**
- Verify type annotations are correct
- Catch type-related bugs before runtime
- Ensure type safety across the codebase
4. **Code Quality Metrics**
- Complexity analysis
- Dead code detection
- Unused import identification
## Context Files
This skill references the following context files in this directory:
- `ruff-configuration.md` - Ruff linting and formatting rules
- `mypy-configuration.md` - Type checking configuration
- `common-issues.md` - Common Python code quality issues and fixes
- `best-practices.md` - Python coding best practices
## Key Tools and Commands
```bash
# Linting
ruff check . # Check for issues
ruff check --fix . # Auto-fix issues
ruff check --watch . # Watch mode
# Formatting
ruff format . # Format all files
ruff format --check . # Check if formatting needed
# Type checking
mypy src # Check types
mypy --strict src # Strict mode
```
## Common Workflows
### Pre-commit Quality Check
```bash
ruff check --fix . && ruff format . && mypy src
```
### CI/CD Pipeline
```bash
ruff check . # Fail if unfixed issues
ruff format --check . # Fail if unformatted
mypy src # Fail on type errors
```
## Expected Outcomes
After using this skill:
- All code follows PEP 8 style guidelines
- Imports are properly organized
- Type hints are correct and comprehensive
- Code is consistently formatted
- Common bugs and issues are identified
## Integration with Other Skills
- Works with `python-project-setup` for initial configuration
- Complements `python-testing` for comprehensive quality assurance
- Used by `python-project-setup` agent for automated checks

View File

@@ -0,0 +1,374 @@
# Mypy Type Checking Configuration
Mypy is a static type checker for Python that helps catch bugs before runtime.
## Basic Configuration
Add to `pyproject.toml`:
```toml
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict_equality = true
show_error_codes = true
show_column_numbers = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = "third_party_package.*"
ignore_missing_imports = true
```
## Strictness Levels
### Minimal (Getting Started)
```toml
[tool.mypy]
python_version = "3.11"
warn_return_any = false
warn_unused_configs = true
```
### Moderate (Recommended)
```toml
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
check_untyped_defs = true
```
### Strict (Maximum Safety)
```toml
[tool.mypy]
strict = true
python_version = "3.11"
```
## Common Configuration Options
### Type Checking Strictness
```toml
# Require type annotations
disallow_untyped_defs = true # Functions must have type hints
disallow_untyped_calls = true # Can't call untyped functions
disallow_incomplete_defs = true # All params must be typed
# Optional handling
no_implicit_optional = true # Optional must be explicit
strict_optional = true # None checking enabled
# Any type usage
disallow_any_unimported = false # Allow Any from untyped imports
disallow_any_expr = false # Forbid Any in expressions
disallow_any_decorated = false # Forbid Any in decorators
disallow_any_explicit = false # Forbid explicit Any
```
### Warning Messages
```toml
warn_return_any = true # Warn on returning Any
warn_redundant_casts = true # Warn on unnecessary casts
warn_unused_ignores = true # Warn on unused type: ignore
warn_unused_configs = true # Warn on unused config options
warn_no_return = true # Warn on missing returns
warn_unreachable = true # Warn on unreachable code
```
### Error Reporting
```toml
show_error_codes = true # Show error codes in messages
show_column_numbers = true # Show column numbers
pretty = true # Pretty print errors
color_output = true # Colorize output
error_summary = true # Show error summary
```
## Type Hints Guide
### Basic Types
```python
from typing import Any, Optional, Union
# Simple types
def greet(name: str) -> str:
return f"Hello, {name}"
# Optional (can be None)
def find_user(user_id: int) -> Optional[User]:
return user_map.get(user_id)
# Modern Optional syntax (Python 3.10+)
def find_user(user_id: int) -> User | None:
return user_map.get(user_id)
# Union types
def process(value: Union[int, str]) -> bool:
return True
# Modern Union syntax (Python 3.10+)
def process(value: int | str) -> bool:
return True
```
### Collections
```python
from typing import List, Dict, Set, Tuple
# Lists
def process_items(items: List[str]) -> List[int]:
return [len(item) for item in items]
# Modern syntax (Python 3.9+)
def process_items(items: list[str]) -> list[int]:
return [len(item) for item in items]
# Dictionaries
def get_config() -> Dict[str, Any]:
return {"key": "value"}
# Modern syntax
def get_config() -> dict[str, Any]:
return {"key": "value"}
# Sets
def unique_items(items: Set[int]) -> int:
return len(items)
# Tuples (fixed size)
def get_coordinates() -> Tuple[float, float]:
return (1.0, 2.0)
# Tuples (variable size)
def get_numbers() -> Tuple[int, ...]:
return (1, 2, 3, 4, 5)
```
### Callable Types
```python
from typing import Callable
# Function that takes int and returns str
Handler = Callable[[int], str]
def process(handler: Handler) -> None:
result = handler(42)
# Multiple parameters
Callback = Callable[[str, int], bool]
# No parameters
Factory = Callable[[], MyClass]
```
### Generic Types
```python
from typing import TypeVar, Generic
T = TypeVar('T')
class Container(Generic[T]):
def __init__(self, value: T) -> None:
self.value = value
def get(self) -> T:
return self.value
# Usage
container: Container[int] = Container(42)
value: int = container.get()
```
### Protocol Types
```python
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
def render(item: Drawable) -> None:
item.draw()
# Any class with draw() method works
class Circle:
def draw(self) -> None:
print("Drawing circle")
render(Circle()) # Type checks!
```
## Commands Reference
```bash
# Check all files
mypy src
# Check specific file
mypy src/module.py
# Strict mode
mypy --strict src
# Show error codes
mypy --show-error-codes src
# Generate type stubs
stubgen -p mypackage -o stubs
# Check against installed packages
mypy --install-types
mypy --non-interactive --install-types
```
## Common Errors and Fixes
### Error: "Argument has incompatible type"
```python
# Problem
def greet(name: str) -> str:
return f"Hello, {name}"
greet(123) # Error: Argument 1 has incompatible type "int"
# Fix: Pass correct type
greet("World")
# Or: Convert type
greet(str(123))
```
### Error: "Function is missing a return statement"
```python
# Problem
def get_value() -> int:
if condition:
return 42
# Missing return!
# Fix: Add return for all paths
def get_value() -> int:
if condition:
return 42
return 0
```
### Error: "Need type annotation"
```python
# Problem
items = [] # Error: Need type annotation
# Fix: Add type annotation
items: list[str] = []
# Or: Initialize with values
items = ["a", "b", "c"]
```
### Error: "Incompatible return type"
```python
# Problem
def get_name() -> str:
return None # Error: Incompatible return
# Fix: Use Optional
def get_name() -> str | None:
return None
```
## Ignoring Errors
```python
# Ignore single line
result = unsafe_function() # type: ignore
# Ignore with reason
result = unsafe_function() # type: ignore[arg-type]
# Ignore entire file
# mypy: ignore-errors
```
## Third-Party Package Stubs
### Installing Type Stubs
```bash
# Install stubs for requests
uv add --dev types-requests
# Common stubs
uv add --dev types-requests
uv add --dev types-redis
uv add --dev types-PyYAML
```
### Handling Missing Stubs
```toml
[[tool.mypy.overrides]]
module = "untyped_package.*"
ignore_missing_imports = true
```
## VSCode Integration
Add to `.vscode/settings.json`:
```json
{
"python.linting.mypyEnabled": true,
"python.linting.enabled": true,
"python.analysis.typeCheckingMode": "basic"
}
```
## Best Practices
1. **Start gradual**: Enable mypy gradually, module by module
2. **Use strict mode**: `strict = true` for new projects
3. **Add type hints everywhere**: Functions, methods, variables
4. **Use Protocol for duck typing**: Better than inheritance
5. **Leverage modern syntax**: Use `list` instead of `List` (Python 3.9+)
6. **Install type stubs**: For all third-party packages
7. **Run in CI**: Fail builds on type errors
8. **Document with types**: Types serve as documentation
## Incremental Adoption
```toml
# Start with specific directories
[tool.mypy]
files = ["src/critical_module"]
strict = true
# Gradually expand
files = ["src/critical_module", "src/api"]
# Eventually cover everything
files = ["src"]
```

View File

@@ -0,0 +1,229 @@
# Ruff Configuration Guide
Ruff is a fast Python linter and formatter that replaces multiple tools (isort, flake8, pyupgrade, etc.).
## Basic Configuration
Add to `pyproject.toml`:
```toml
[tool.ruff]
line-length = 100
target-version = "py311"
src = ["src", "tests"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ARG", # flake8-unused-arguments
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
"RUF", # Ruff-specific rules
]
ignore = [
"E501", # line too long (handled by formatter)
"B008", # do not perform function calls in argument defaults
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = [
"ARG", # Unused function arguments allowed in tests
"S101", # Assert allowed in tests
]
"__init__.py" = [
"F401", # Unused imports allowed in __init__.py
]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"
[tool.ruff.lint.isort]
known-first-party = ["project_name"]
```
## Rule Categories
### Error Detection (E, W, F)
- **E**: PEP 8 style errors
- **W**: PEP 8 style warnings
- **F**: PyFlakes logical errors
### Code Quality (B, C4, SIM)
- **B**: Bugbear - likely bugs and design problems
- **C4**: Comprehensions - simplify list/dict/set comprehensions
- **SIM**: Simplify - code simplification suggestions
### Modernization (UP)
- **UP**: PyUpgrade - use modern Python syntax
- `typing.List``list`
- `Optional[str]``str | None` (Python 3.10+)
### Organization (I)
- **I**: isort - import sorting and organization
### Performance (ARG, TCH)
- **ARG**: Detect unused function arguments
- **TCH**: Type checking imports (move to TYPE_CHECKING block)
## Common Rules
### E/W Series (PEP 8)
- `E111`: Indentation is not a multiple of 4
- `E201`: Whitespace after '('
- `E202`: Whitespace before ')'
- `E203`: Whitespace before ':'
- `E302`: Expected 2 blank lines
- `E303`: Too many blank lines
- `W291`: Trailing whitespace
### F Series (PyFlakes)
- `F401`: Unused import
- `F811`: Redefinition of unused name
- `F821`: Undefined name
- `F841`: Local variable assigned but never used
### B Series (Bugbear)
- `B002`: Using * in exception handling
- `B006`: Mutable default argument
- `B007`: Loop variable not used
- `B008`: Function calls in default arguments
- `B904`: Use `raise ... from ...` for exception chaining
### UP Series (PyUpgrade)
- `UP006`: Use `list` instead of `typing.List`
- `UP007`: Use `X | Y` instead of `typing.Union[X, Y]`
- `UP032`: Use f-strings instead of `.format()`
## Commands Reference
### Linting
```bash
# Check all files
ruff check .
# Check specific file
ruff check src/module.py
# Auto-fix issues
ruff check --fix .
# Show fixes without applying
ruff check --diff .
# Watch mode (re-run on file changes)
ruff check --watch .
# Output JSON format
ruff check --output-format=json .
```
### Formatting
```bash
# Format all files
ruff format .
# Format specific file
ruff format src/module.py
# Check if formatting needed (CI)
ruff format --check .
# Show diff without applying
ruff format --diff .
```
### Configuration Testing
```bash
# Show active configuration
ruff check --show-settings
# Show all available rules
ruff rule --all
# Show specific rule documentation
ruff rule F401
```
## Per-File Ignores
Ignore specific rules for specific files:
```toml
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["ARG", "S101"] # Tests can use assert and unused args
"__init__.py" = ["F401"] # Unused imports OK in __init__
"scripts/**/*.py" = ["T201"] # print() OK in scripts
"migrations/**/*.py" = ["E501"] # Long lines OK in migrations
```
## VSCode Integration
Add to `.vscode/settings.json`:
```json
{
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll": "explicit"
}
},
"ruff.enable": true,
"ruff.lint.enable": true,
"ruff.format.enable": true
}
```
## Performance Tips
Ruff is extremely fast (10-100x faster than alternatives):
- Written in Rust for performance
- Parallel processing by default
- Incremental linting in watch mode
- Caching for repeated runs
## Migration from Other Tools
### From Black
Ruff's formatter is compatible with Black:
```bash
ruff format . # Replaces: black .
```
### From isort
```bash
ruff check --select I --fix . # Replaces: isort .
```
### From flake8
```bash
ruff check . # Replaces: flake8 .
```
### From pyupgrade
```bash
ruff check --select UP --fix . # Replaces: pyupgrade .
```
## Best Practices
1. **Run formatter last**: `ruff check --fix . && ruff format .`
2. **Use in CI**: Check both linting and formatting
3. **Configure per-file ignores**: For tests, init files, migrations
4. **Enable auto-fix**: Most issues can be fixed automatically
5. **Use with pre-commit**: Run checks before commits
6. **Review rule changes**: Stay updated with new releases

View File

@@ -0,0 +1,76 @@
# Python Project Setup Skill
Set up new Python projects following modern best practices with uv, ruff, pytest, and proper project structure.
## When to Use This Skill
Use this skill when:
- User requests to create a new Python project
- User wants to initialize Python tooling in an existing directory
- User mentions setting up a Python development environment
- User asks about Python project structure or best practices
## Core Capabilities
1. **Project Initialization**
- Initialize projects with `uv init`
- Set up proper directory structure
- Configure Python version with pyenv
2. **Dependency Management**
- Install and configure uv package manager
- Add essential dependencies (pytest, ruff, mypy, pydantic)
- Set up development vs runtime dependencies
3. **Configuration Files**
- Create `pyproject.toml` with tool configurations
- Set up `.gitignore` for Python projects
- Configure VSCode settings for Python development
- Create `.python-version` for version management
4. **Project Structure**
- Create `src/<project_name>/` for source code
- Set up `tests/` directory for test files
- Create `docs/` for documentation
- Set up `.vscode/` for editor configuration
## Context Files
This skill references the following context files in this directory:
- `project-structure-template.md` - Standard directory layout
- `pyproject-toml-template.md` - Configuration template
- `vscode-settings-template.json` - Editor configuration
- `gitignore-template.md` - Python gitignore patterns
- `readme-template.md` - README structure
## Key Tools and Commands
```bash
# Project initialization
uv init <project-name>
# Dependency management
uv add pytest --dev
uv add ruff --dev
uv add mypy --dev
uv add pydantic
# Sync dependencies
uv sync
```
## Expected Outcomes
After using this skill, the user should have:
- A fully initialized Python project with modern tooling
- Proper project structure following best practices
- All essential dependencies installed
- Configuration files set up correctly
- Version control initialized (git)
- Documentation scaffolding in place
## Integration with Other Skills
- Works with `python-code-quality` skill for linting setup
- Works with `python-testing` skill for test framework configuration
- Complements the `python-project-setup` agent for full orchestration

View File

@@ -0,0 +1,209 @@
# Python .gitignore Template
```gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
Pipfile.lock
# poetry
poetry.lock
# pdm
.pdm.toml
# PEP 582
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# Ruff
.ruff_cache/
# uv
uv.lock
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Project specific
*.db
*.sqlite
*.sqlite3
data/
logs/
tmp/
temp/
```
## Explanation of Key Patterns
### Python Runtime Files
- `__pycache__/` - Compiled bytecode cache
- `*.pyc`, `*.pyo`, `*.pyd` - Compiled Python files
- `*.so` - Compiled C extensions
### Package/Build Artifacts
- `dist/`, `build/` - Build output directories
- `*.egg-info/` - Package metadata
- `.eggs/` - Installed packages directory
### Virtual Environments
- `.venv/`, `venv/`, `env/` - Virtual environment directories
- Always create virtual environments, never commit them
### Testing & Coverage
- `.pytest_cache/` - Pytest cache
- `.coverage` - Coverage data files
- `htmlcov/` - HTML coverage reports
### Type Checking & Linting
- `.mypy_cache/` - Mypy cache
- `.ruff_cache/` - Ruff cache
- `.pytype/` - Pytype cache
### Documentation
- `docs/_build/` - Sphinx build output
- `/site` - MkDocs build output
### Environment Variables
- `.env` - Environment variable files (NEVER commit these)
- Should contain secrets, API keys, database URLs
### IDE Files
- `.vscode/` - VSCode settings (some teams commit this)
- `.idea/` - PyCharm settings
- `.DS_Store` - macOS file system metadata
## Customization Tips
1. **Commit `.vscode/` if desired**: Remove from .gitignore to share editor settings
2. **Project-specific data**: Add custom directories for data files, logs, etc.
3. **Lock files**: Consider keeping `uv.lock` for reproducible builds
4. **Documentation**: Remove `docs/_build/` if you want to commit built docs

View File

@@ -0,0 +1,112 @@
# Python Project Structure Template
## Standard Directory Layout
```
project_name/
├── .vscode/ # VSCode editor configuration
│ ├── settings.json # Python, ruff, mypy settings
│ └── launch.json # Debug configurations
├── src/ # Source code directory
│ └── project_name/ # Main package (use underscores)
│ ├── __init__.py # Package initialization
│ ├── main.py # Entry point (if applicable)
│ ├── models/ # Data models (Pydantic)
│ ├── services/ # Business logic
│ ├── api/ # API routes (if using FastAPI)
│ └── utils/ # Utility functions
├── tests/ # Test directory
│ ├── __init__.py
│ ├── conftest.py # Pytest fixtures
│ ├── test_main.py # Test files (prefix with test_)
│ └── integration/ # Integration tests
├── docs/ # Documentation
│ ├── index.md
│ └── api/ # API documentation
├── .github/ # GitHub configuration
│ ├── workflows/ # CI/CD workflows
│ │ └── test.yml
│ └── dependabot.yml # Dependency updates
├── .gitignore # Git ignore patterns
├── .python-version # Python version for pyenv
├── pyproject.toml # Project metadata and dependencies
├── README.md # Project documentation
└── uv.lock # Locked dependencies (generated)
```
## File Naming Conventions
- **Python packages**: Use underscores (e.g., `my_package`)
- **Project names**: Use underscores (e.g., `my_project`)
- **Test files**: Prefix with `test_` (e.g., `test_models.py`)
- **Private modules**: Prefix with `_` (e.g., `_internal.py`)
## Directory Purpose
### `src/project_name/`
Main application code organized by function:
- `models/` - Data classes, Pydantic models, database models
- `services/` - Business logic and service layer
- `api/` - API endpoints and route handlers (FastAPI)
- `utils/` - Helper functions and utilities
- `config/` - Configuration management
### `tests/`
Test files mirroring the structure of `src/`:
- Unit tests for each module
- Integration tests in `integration/` subdirectory
- `conftest.py` for shared fixtures
- Use pytest conventions
### `docs/`
Project documentation:
- User guides
- API documentation
- Architecture decisions
- Setup instructions
## Module Organization
Each Python module should follow this pattern:
```python
# frozen_string_literal equivalent in Python
"""Module docstring describing purpose."""
from typing import Any, Optional
import third_party_package
from project_name import local_module
class MyClass:
"""Class docstring."""
def __init__(self, param: str) -> None:
"""Initialize with param."""
self.param = param
def method(self) -> str:
"""Method docstring."""
return self.param
def public_function() -> None:
"""Public function docstring."""
pass
def _private_function() -> None:
"""Private helper function."""
pass
```
## Best Practices
1. **Use `src/` layout**: Prevents accidental imports of local code
2. **Type hints everywhere**: Add type annotations to all functions
3. **Docstrings**: Document all public classes and functions
4. **Test organization**: Mirror source structure in tests
5. **Configuration**: Use environment variables or config files, never hardcode
6. **Dependencies**: Separate dev dependencies from runtime dependencies

View File

@@ -0,0 +1,226 @@
# pyproject.toml Template
## Complete Configuration Template
```toml
[project]
name = "project-name"
version = "0.1.0"
description = "Project description"
readme = "README.md"
requires-python = ">=3.11"
license = {text = "MIT"}
authors = [
{name = "Your Name", email = "you@example.com"}
]
keywords = ["python", "example"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"pydantic>=2.0.0",
# Add runtime dependencies here
]
[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-cov>=4.1.0",
"ruff>=0.3.0",
"mypy>=1.8.0",
]
api = [
"fastapi>=0.110.0",
"uvicorn[standard]>=0.27.0",
]
docs = [
"mkdocs>=1.5.0",
"mkdocs-material>=9.5.0",
]
[project.urls]
Homepage = "https://github.com/username/project-name"
Repository = "https://github.com/username/project-name"
Documentation = "https://project-name.readthedocs.io"
Issues = "https://github.com/username/project-name/issues"
[project.scripts]
# Define CLI entry points
project-cli = "project_name.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/project_name"]
# Ruff configuration
[tool.ruff]
line-length = 100
target-version = "py311"
src = ["src", "tests"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ARG", # flake8-unused-arguments
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
]
ignore = [
"E501", # line too long (handled by formatter)
"B008", # do not perform function calls in argument defaults
"B904", # raise from None
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = [
"ARG", # Unused function arguments allowed in tests
"S101", # Assert allowed in tests
]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"
# Pytest configuration
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--showlocals",
"-ra",
]
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"integration: marks tests as integration tests",
]
# Coverage configuration
[tool.coverage.run]
source = ["src"]
branch = true
omit = [
"*/tests/*",
"*/__init__.py",
]
[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"@abstractmethod",
]
# Mypy configuration
[tool.mypy]
python_version = "3.11"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
strict_equality = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
# UV-specific configuration (if needed)
[tool.uv]
dev-dependencies = [
"pytest>=8.0.0",
"pytest-cov>=4.1.0",
"ruff>=0.3.0",
"mypy>=1.8.0",
]
```
## Configuration Sections Explained
### `[project]`
Basic project metadata used by build tools and package indexes.
### `[project.optional-dependencies]`
Groups of optional dependencies for different use cases:
- `dev` - Development tools (testing, linting)
- `api` - API framework dependencies (FastAPI, uvicorn)
- `docs` - Documentation generation tools
### `[tool.ruff]`
Ruff linter and formatter configuration:
- Fast Python linter combining multiple tools
- Replaces isort, flake8, pyupgrade, and more
- Automatic code formatting
### `[tool.pytest.ini_options]`
Pytest test framework configuration:
- Test discovery patterns
- Default command-line options
- Custom test markers
### `[tool.coverage]`
Code coverage settings for pytest-cov:
- Which files to measure
- Coverage reporting options
- Lines to exclude from coverage
### `[tool.mypy]`
Type checking configuration:
- Strictness settings
- Type checking rules
- Per-directory overrides
## Usage Examples
```bash
# Install project with dev dependencies
uv sync --extra dev
# Install with API dependencies
uv sync --extra api
# Install all optional dependencies
uv sync --all-extras
# Run tests with coverage
uv run pytest --cov
# Lint code
uv run ruff check .
# Format code
uv run ruff format .
# Type check
uv run mypy src
```

View File

@@ -0,0 +1,332 @@
# README.md Template
```markdown
# Project Name
Brief one-line description of what this project does.
## Overview
A more detailed description of the project, its purpose, and key features.
## Features
- Feature 1: Description
- Feature 2: Description
- Feature 3: Description
## Requirements
- Python 3.11 or higher
- uv package manager
## Installation
### Using uv (Recommended)
\`\`\`bash
# Clone the repository
git clone https://github.com/username/project-name.git
cd project-name
# Install dependencies
uv sync
# Activate virtual environment (optional, uv runs commands automatically)
source .venv/bin/activate # On Unix/macOS
.venv\Scripts\activate # On Windows
\`\`\`
### Using pip
\`\`\`bash
pip install -e .
\`\`\`
## Quick Start
\`\`\`python
from project_name import main_function
# Example usage
result = main_function()
print(result)
\`\`\`
## Usage
### Command Line Interface
\`\`\`bash
# Run the main application
uv run python -m project_name
# Or if you installed with pip
project-cli --help
\`\`\`
### As a Library
\`\`\`python
from project_name import SomeClass
# Create an instance
instance = SomeClass(param="value")
# Use the instance
result = instance.method()
\`\`\`
## Development
### Setup Development Environment
\`\`\`bash
# Install with development dependencies
uv sync --extra dev
# Or install all extras
uv sync --all-extras
\`\`\`
### Running Tests
\`\`\`bash
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov
# Run specific test file
uv run pytest tests/test_specific.py
# Run with verbose output
uv run pytest -v
\`\`\`
### Code Quality
\`\`\`bash
# Lint code
uv run ruff check .
# Fix auto-fixable issues
uv run ruff check --fix .
# Format code
uv run ruff format .
# Type checking
uv run mypy src
\`\`\`
### Pre-commit Checks
Before committing, ensure all checks pass:
\`\`\`bash
# Run all checks
uv run ruff check . && uv run ruff format . && uv run mypy src && uv run pytest
\`\`\`
## Project Structure
\`\`\`
project-name/
├── src/
│ └── project_name/ # Main package
│ ├── __init__.py
│ ├── main.py # Entry point
│ ├── models/ # Data models
│ ├── services/ # Business logic
│ └── utils/ # Utilities
├── tests/ # Test files
├── docs/ # Documentation
├── pyproject.toml # Project configuration
└── README.md # This file
\`\`\`
## Configuration
### Environment Variables
Create a `.env` file in the project root:
\`\`\`env
API_KEY=your_api_key_here
DATABASE_URL=postgresql://user:pass@localhost/dbname
DEBUG=false
\`\`\`
### Configuration File
Alternatively, create a `config.yaml`:
\`\`\`yaml
api:
key: your_api_key
timeout: 30
database:
url: postgresql://user:pass@localhost/dbname
pool_size: 5
\`\`\`
## API Documentation
### Main Classes
#### `SomeClass`
Description of the class.
\`\`\`python
from project_name import SomeClass
instance = SomeClass(param="value")
result = instance.method()
\`\`\`
**Parameters:**
- `param` (str): Description of parameter
**Returns:**
- `str`: Description of return value
### Functions
#### `main_function()`
Description of the function.
\`\`\`python
from project_name import main_function
result = main_function()
\`\`\`
## API Endpoints (if applicable)
### Start the Server
\`\`\`bash
uv run uvicorn project_name.api.main:app --reload
\`\`\`
### Endpoints
#### GET `/api/items`
Get all items.
**Response:**
\`\`\`json
{
"items": [
{"id": 1, "name": "Item 1"},
{"id": 2, "name": "Item 2"}
]
}
\`\`\`
#### POST `/api/items`
Create a new item.
**Request Body:**
\`\`\`json
{
"name": "New Item"
}
\`\`\`
**Response:**
\`\`\`json
{
"id": 3,
"name": "New Item"
}
\`\`\`
## Contributing
Contributions are welcome! Please follow these steps:
1. Fork the repository
2. Create a feature branch (\`git checkout -b feature/amazing-feature\`)
3. Make your changes
4. Run tests and linting (\`uv run pytest && uv run ruff check .\`)
5. Commit your changes (\`git commit -m 'Add amazing feature'\`)
6. Push to the branch (\`git push origin feature/amazing-feature\`)
7. Open a Pull Request
### Code Style
- Follow PEP 8 guidelines
- Use type hints for all functions
- Write docstrings for all public APIs
- Maintain test coverage above 80%
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Authors
- Your Name - [@yourhandle](https://github.com/yourhandle)
## Acknowledgments
- Library or resource that inspired this project
- Contributors who helped
- Any other acknowledgments
## Changelog
### [0.1.0] - 2024-01-01
#### Added
- Initial release
- Basic functionality
#### Changed
- Nothing yet
#### Fixed
- Nothing yet
## Support
For support, email support@example.com or open an issue on GitHub.
## Links
- [Documentation](https://project-name.readthedocs.io)
- [Issue Tracker](https://github.com/username/project-name/issues)
- [Changelog](CHANGELOG.md)
```
## Sections Explained
1. **Title & Description**: Clear project name and one-line summary
2. **Overview**: More detailed description of purpose and features
3. **Installation**: Step-by-step setup instructions
4. **Quick Start**: Minimal example to get started quickly
5. **Usage**: Detailed usage examples (CLI and library)
6. **Development**: Instructions for contributors
7. **Project Structure**: Overview of codebase organization
8. **Configuration**: How to configure the application
9. **API Documentation**: Documentation of public API
10. **Contributing**: Guidelines for contributions
11. **License & Authors**: Legal and attribution information
12. **Support**: How to get help
## Tips for Good READMEs
- **Keep it concise**: Users should understand the project in 30 seconds
- **Working examples**: All code examples should be copy-paste ready
- **Clear installation**: Step-by-step instructions that actually work
- **Visual aids**: Consider adding screenshots, diagrams, or GIFs
- **Badges**: Add CI status, coverage, and version badges
- **Update regularly**: Keep it in sync with the actual codebase

View File

@@ -0,0 +1,54 @@
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true,
"python.analysis.diagnosticMode": "workspace",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll": "explicit"
},
"editor.rulers": [100]
},
"ruff.enable": true,
"ruff.lint.enable": true,
"ruff.format.enable": true,
"ruff.organizeImports": true,
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestArgs": [
"tests"
],
"files.exclude": {
"**/__pycache__": true,
"**/*.pyc": true,
"**/*.pyo": true,
"**/.pytest_cache": true,
"**/.mypy_cache": true,
"**/.ruff_cache": true,
"**/*.egg-info": true
},
"files.watcherExclude": {
"**/__pycache__/**": true,
"**/.venv/**": true,
"**/.pytest_cache/**": true,
"**/.mypy_cache/**": true,
"**/.ruff_cache/**": true
},
"search.exclude": {
"**/.venv": true,
"**/node_modules": true,
"**/__pycache__": true,
"**/.pytest_cache": true,
"**/.mypy_cache": true,
"**/.ruff_cache": true
}
}

View File

@@ -0,0 +1,111 @@
# Python Testing Skill
Comprehensive testing with pytest including unit tests, integration tests, fixtures, and coverage reporting.
## When to Use This Skill
Use this skill when:
- User requests to run tests
- User wants to create new tests
- User asks about test coverage
- User mentions pytest, unit tests, or integration tests
- Debugging test failures
## Core Capabilities
1. **Test Execution**
- Run all tests or specific test files
- Run tests matching patterns
- Run only failed tests
- Generate test reports
2. **Test Coverage**
- Measure code coverage
- Generate HTML coverage reports
- Identify untested code
- Set coverage thresholds
3. **Test Writing**
- Create unit tests for functions and classes
- Write integration tests
- Use pytest fixtures effectively
- Implement parametrized tests
4. **Test Organization**
- Structure test files properly
- Use conftest.py for shared fixtures
- Organize tests by feature or module
- Mark tests for selective execution
## Context Files
This skill references the following context files in this directory:
- `pytest-configuration.md` - Pytest setup and configuration
- `test-patterns.md` - Common testing patterns and examples
- `fixtures-guide.md` - Using pytest fixtures
- `coverage-guide.md` - Code coverage best practices
## Key Tools and Commands
```bash
# Run tests
uv run pytest # All tests
uv run pytest tests/test_file.py # Specific file
uv run pytest -k "pattern" # Tests matching pattern
uv run pytest --lf # Last failed only
# Coverage
uv run pytest --cov # With coverage
uv run pytest --cov --cov-report=html # HTML report
uv run pytest --cov --cov-report=term-missing # Show missing lines
# Output control
uv run pytest -v # Verbose
uv run pytest -s # Show print statements
uv run pytest -x # Stop on first failure
```
## Common Test Patterns
### Unit Test Example
```python
def test_addition():
assert add(2, 3) == 5
assert add(-1, 1) == 0
```
### Fixture Usage
```python
@pytest.fixture
def sample_data():
return {"key": "value"}
def test_with_fixture(sample_data):
assert sample_data["key"] == "value"
```
### Parametrized Tests
```python
@pytest.mark.parametrize("input,expected", [
(2, 4),
(3, 9),
(4, 16),
])
def test_square(input, expected):
assert square(input) == expected
```
## Expected Outcomes
After using this skill:
- Comprehensive test suite covering critical functionality
- High test coverage (ideally > 80%)
- Well-organized test files
- Clear test failure messages
- Fast test execution
## Integration with Other Skills
- Works with `python-project-setup` for test directory structure
- Complements `python-code-quality` for comprehensive QA
- Used by `python-project-setup` agent for automated testing

View File

@@ -0,0 +1,447 @@
# Pytest Configuration Guide
Pytest is a powerful testing framework for Python that makes it easy to write simple and scalable tests.
## Installation
```bash
uv add --dev pytest
uv add --dev pytest-cov # For coverage
uv add --dev pytest-mock # For mocking
uv add --dev pytest-asyncio # For async tests
```
## Basic Configuration
Add to `pyproject.toml`:
```toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--showlocals",
"-ra",
]
markers = [
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
"integration: marks tests as integration tests",
"unit: marks tests as unit tests",
]
```
## Directory Structure
```
project/
├── src/
│ └── project_name/
│ └── module.py
├── tests/
│ ├── __init__.py
│ ├── conftest.py # Shared fixtures
│ ├── test_module.py # Unit tests
│ ├── integration/
│ │ ├── __init__.py
│ │ └── test_api.py # Integration tests
│ └── fixtures/
│ └── sample_data.json
```
## Writing Tests
### Basic Test Function
```python
def test_simple_addition():
"""Test that addition works correctly."""
result = add(2, 3)
assert result == 5
```
### Test Class
```python
class TestCalculator:
"""Tests for Calculator class."""
def test_addition(self):
calc = Calculator()
assert calc.add(2, 3) == 5
def test_subtraction(self):
calc = Calculator()
assert calc.subtract(5, 3) == 2
```
### Using Assertions
```python
def test_assertions():
# Equality
assert result == expected
# Boolean
assert is_valid()
assert not is_invalid()
# Membership
assert item in collection
assert key in dictionary
# Type checking
assert isinstance(obj, MyClass)
# Exceptions
with pytest.raises(ValueError):
raise_error()
# Approximate equality (floats)
assert result == pytest.approx(expected, rel=1e-5)
```
## Fixtures
### Basic Fixture
```python
import pytest
@pytest.fixture
def sample_user():
"""Create a sample user for testing."""
return User(name="Alice", age=30)
def test_user_greeting(sample_user):
assert sample_user.greet() == "Hello, I'm Alice"
```
### Fixture Scopes
```python
@pytest.fixture(scope="function") # Default, new instance per test
def function_scope():
return setup()
@pytest.fixture(scope="class") # One instance per test class
def class_scope():
return setup()
@pytest.fixture(scope="module") # One instance per module
def module_scope():
return setup()
@pytest.fixture(scope="session") # One instance per test session
def session_scope():
return setup()
```
### Fixture Cleanup
```python
@pytest.fixture
def resource():
# Setup
res = acquire_resource()
yield res
# Teardown
res.cleanup()
# Or using context manager
@pytest.fixture
def database():
with create_database() as db:
yield db
# Automatic cleanup
```
### Fixture Dependencies
```python
@pytest.fixture
def database():
return Database()
@pytest.fixture
def user_repository(database):
return UserRepository(database)
def test_find_user(user_repository):
user = user_repository.find(1)
assert user is not None
```
## Parametrized Tests
### Basic Parametrization
```python
@pytest.mark.parametrize("input,expected", [
(2, 4),
(3, 9),
(4, 16),
(5, 25),
])
def test_square(input, expected):
assert square(input) == expected
```
### Multiple Parameters
```python
@pytest.mark.parametrize("a,b,expected", [
(1, 1, 2),
(2, 3, 5),
(10, -5, 5),
])
def test_addition(a, b, expected):
assert add(a, b) == expected
```
### Parametrizing Fixtures
```python
@pytest.fixture(params=[1, 2, 3])
def number(request):
return request.param
def test_with_different_numbers(number):
assert number > 0
```
## Test Markers
### Built-in Markers
```python
@pytest.mark.skip(reason="Not implemented yet")
def test_future_feature():
pass
@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python 3.10+")
def test_new_feature():
pass
@pytest.mark.xfail(reason="Known bug")
def test_buggy_feature():
pass
```
### Custom Markers
```python
# Define in pyproject.toml
# markers = ["slow: marks tests as slow"]
@pytest.mark.slow
def test_expensive_operation():
# Long-running test
pass
# Run with: pytest -m "not slow"
```
## Exception Testing
```python
def test_raises_value_error():
with pytest.raises(ValueError):
raise ValueError("Invalid value")
def test_raises_with_message():
with pytest.raises(ValueError, match="Invalid"):
raise ValueError("Invalid value")
def test_exception_info():
with pytest.raises(ValueError) as exc_info:
raise ValueError("Invalid value")
assert "Invalid" in str(exc_info.value)
```
## Mocking
### Using pytest-mock
```python
def test_with_mock(mocker):
# Mock a function
mock = mocker.patch('module.function')
mock.return_value = 42
result = call_function()
assert result == 42
mock.assert_called_once()
def test_mock_method(mocker):
# Mock a method
mock = mocker.patch.object(MyClass, 'method')
mock.return_value = "mocked"
obj = MyClass()
assert obj.method() == "mocked"
```
### Using unittest.mock
```python
from unittest.mock import Mock, patch
def test_with_mock():
with patch('module.function') as mock:
mock.return_value = 42
result = call_function()
assert result == 42
```
## Async Tests
```python
import pytest
@pytest.mark.asyncio
async def test_async_function():
result = await async_operation()
assert result == expected
@pytest.fixture
async def async_client():
client = AsyncClient()
yield client
await client.close()
@pytest.mark.asyncio
async def test_with_async_fixture(async_client):
result = await async_client.get("/endpoint")
assert result.status_code == 200
```
## Running Tests
```bash
# All tests
pytest
# Specific file
pytest tests/test_module.py
# Specific test
pytest tests/test_module.py::test_function
# Specific class
pytest tests/test_module.py::TestClass
# Pattern matching
pytest -k "test_user"
# Markers
pytest -m slow # Only slow tests
pytest -m "not slow" # Exclude slow tests
# Last failed
pytest --lf
# Failed first
pytest --ff
# Stop on first failure
pytest -x
# Stop after N failures
pytest --maxfail=3
# Verbose output
pytest -v
# Show print statements
pytest -s
# Show locals in tracebacks
pytest --showlocals
# Parallel execution (requires pytest-xdist)
pytest -n auto
```
## conftest.py
Shared configuration and fixtures:
```python
# tests/conftest.py
import pytest
@pytest.fixture(scope="session")
def database():
"""Create database for entire test session."""
db = create_test_database()
yield db
db.drop()
@pytest.fixture(autouse=True)
def reset_database(database):
"""Reset database before each test."""
database.clear()
def pytest_configure(config):
"""Pytest configuration hook."""
config.addinivalue_line(
"markers", "integration: mark test as integration test"
)
```
## Best Practices
1. **One assertion per test**: Keep tests focused
2. **Descriptive names**: Use `test_user_creation_with_valid_data`
3. **AAA pattern**: Arrange, Act, Assert
4. **Use fixtures**: Avoid duplicating setup code
5. **Test edge cases**: Not just happy paths
6. **Fast tests**: Keep unit tests under 100ms
7. **Independent tests**: Tests should not depend on each other
8. **Clear assertions**: Make failures obvious
## Example Test File
```python
"""Tests for user module."""
import pytest
from project_name.models import User
@pytest.fixture
def valid_user_data():
"""Valid user data for testing."""
return {
"name": "Alice",
"email": "alice@example.com",
"age": 30
}
class TestUser:
"""Tests for User model."""
def test_create_user(self, valid_user_data):
"""Test creating a user with valid data."""
# Arrange & Act
user = User(**valid_user_data)
# Assert
assert user.name == "Alice"
assert user.email == "alice@example.com"
assert user.age == 30
def test_user_greeting(self, valid_user_data):
"""Test user greeting message."""
user = User(**valid_user_data)
assert user.greet() == "Hello, I'm Alice"
@pytest.mark.parametrize("age", [-1, 0, 151])
def test_invalid_age(self, valid_user_data, age):
"""Test that invalid ages raise ValueError."""
valid_user_data["age"] = age
with pytest.raises(ValueError, match="Invalid age"):
User(**valid_user_data)
```