chore(skills): добавлены скиллы docstring и security-audit
- Зачем:
- напоминание моделям про Google-style docstring'ы и Ruff-конфиг.
- чеклист безопасности для ревью кода и зависимостей.
- Что:
- добавлен docstring (DeerHide): вычищены мёртвые ссылки, добавлен Ruff select, правило языка проекта.
- добавлен security-audit: чеклист OWASP, severity-уровни, формат отчёта.
- убраны test-generator (Node.js стек) и api-docs (избыточен).
- Проверка:
- ls agents/.agents/skills/{docstring,security-audit}/SKILL.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
168b40ddd7
commit
77adaad441
2 files changed
+673
No files matched your search
@@ -0,0 +1,445 @@
|
||||
---
|
||||
name: python-docstring
|
||||
description: A skill for writing consistent, well-structured Python docstrings following the Google style convention.
|
||||
metadata:
|
||||
author: Deerhide
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Python Docstring Skill
|
||||
|
||||
## When to use this skill?
|
||||
|
||||
- Use this skill when writing or updating Python documentation strings (docstrings).
|
||||
- This skill is useful for ensuring consistent documentation across a Python codebase.
|
||||
- It should be applied when documenting modules, classes, functions, methods, and attributes.
|
||||
- Use it during code reviews to verify docstring quality and completeness.
|
||||
|
||||
# Language
|
||||
|
||||
Язык docstring'ов определяется по проекту: если в существующем коде комментарии и docstring'ы на русском — пиши на русском, если на английском — на английском. Не смешивай языки внутри одного проекта.
|
||||
|
||||
# Docstring Style Convention
|
||||
|
||||
We follow the **Google style** docstring convention. This style is chosen for its:
|
||||
- Readability and clean formatting
|
||||
- Wide adoption in the Python community
|
||||
- Excellent support in documentation tools (Sphinx, MkDocs)
|
||||
- Native support in linting tools (Ruff, Pylint)
|
||||
|
||||
# Module Docstrings
|
||||
|
||||
Every Python module should start with a docstring describing its purpose.
|
||||
|
||||
```python
|
||||
"""Module for handling user authentication and session management.
|
||||
|
||||
This module provides utilities for user authentication, token generation,
|
||||
and session lifecycle management. It integrates with OAuth2 providers
|
||||
and supports JWT-based authentication.
|
||||
|
||||
Example:
|
||||
Basic usage of the authentication service::
|
||||
|
||||
from auth import AuthService
|
||||
|
||||
service = AuthService(secret_key="my-secret")
|
||||
token = service.create_token(user_id=123)
|
||||
|
||||
Attributes:
|
||||
DEFAULT_TOKEN_EXPIRY (int): Default token expiration time in seconds.
|
||||
SUPPORTED_ALGORITHMS (list): List of supported JWT algorithms.
|
||||
|
||||
Todo:
|
||||
* Add support for refresh tokens
|
||||
* Implement rate limiting for failed attempts
|
||||
"""
|
||||
|
||||
DEFAULT_TOKEN_EXPIRY = 3600
|
||||
SUPPORTED_ALGORITHMS = ["HS256", "RS256"]
|
||||
```
|
||||
|
||||
# Class Docstrings
|
||||
|
||||
Class docstrings should describe the class purpose and list its attributes.
|
||||
|
||||
```python
|
||||
class UserRepository:
|
||||
"""Repository for managing user data persistence.
|
||||
|
||||
This class provides CRUD operations for user entities and handles
|
||||
database connections and transactions.
|
||||
|
||||
Attributes:
|
||||
connection: Database connection instance.
|
||||
table_name: Name of the users table.
|
||||
cache_enabled: Whether caching is enabled for queries.
|
||||
|
||||
Example:
|
||||
Creating and using a repository::
|
||||
|
||||
repo = UserRepository(connection=db_conn)
|
||||
user = repo.find_by_id(user_id=42)
|
||||
repo.save(user)
|
||||
"""
|
||||
|
||||
def __init__(self, connection: DatabaseConnection) -> None:
|
||||
"""Initialize the UserRepository.
|
||||
|
||||
Args:
|
||||
connection: Active database connection to use for operations.
|
||||
"""
|
||||
self.connection = connection
|
||||
self.table_name = "users"
|
||||
self.cache_enabled = True
|
||||
```
|
||||
|
||||
# Function and Method Docstrings
|
||||
|
||||
Functions and methods should document their purpose, arguments, return values, and exceptions.
|
||||
|
||||
## Basic Function
|
||||
|
||||
```python
|
||||
def calculate_discount(price: float, percentage: float) -> float:
|
||||
"""Calculate the discounted price.
|
||||
|
||||
Args:
|
||||
price: Original price of the item.
|
||||
percentage: Discount percentage (0-100).
|
||||
|
||||
Returns:
|
||||
The discounted price after applying the percentage reduction.
|
||||
|
||||
Raises:
|
||||
ValueError: If percentage is not between 0 and 100.
|
||||
TypeError: If price or percentage are not numeric.
|
||||
|
||||
Example:
|
||||
>>> calculate_discount(100.0, 20)
|
||||
80.0
|
||||
"""
|
||||
if not 0 <= percentage <= 100:
|
||||
raise ValueError("Percentage must be between 0 and 100")
|
||||
return price * (1 - percentage / 100)
|
||||
```
|
||||
|
||||
## Method with Complex Arguments
|
||||
|
||||
```python
|
||||
def create_user(
|
||||
self,
|
||||
username: str,
|
||||
email: str,
|
||||
*,
|
||||
role: str = "user",
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> User:
|
||||
"""Create a new user in the system.
|
||||
|
||||
Creates a user entity with the provided information and persists
|
||||
it to the database. Sends a welcome email upon successful creation.
|
||||
|
||||
Args:
|
||||
username: Unique username for the new user. Must be alphanumeric
|
||||
and between 3-50 characters.
|
||||
email: Valid email address for the user.
|
||||
role: User role assignment. Defaults to "user".
|
||||
Valid options are "user", "admin", "moderator".
|
||||
metadata: Optional dictionary of additional user metadata.
|
||||
Keys must be strings, values can be any JSON-serializable type.
|
||||
|
||||
Returns:
|
||||
The newly created User instance with assigned ID.
|
||||
|
||||
Raises:
|
||||
DuplicateUserError: If username or email already exists.
|
||||
ValidationError: If input validation fails.
|
||||
DatabaseError: If database operation fails.
|
||||
|
||||
Note:
|
||||
This method triggers a background task for sending the welcome email.
|
||||
The email sending is non-blocking and failures are logged but not raised.
|
||||
|
||||
Example:
|
||||
Creating a basic user::
|
||||
|
||||
user = service.create_user(
|
||||
username="johndoe",
|
||||
email="john@example.com"
|
||||
)
|
||||
|
||||
Creating an admin user with metadata::
|
||||
|
||||
admin = service.create_user(
|
||||
username="admin",
|
||||
email="admin@example.com",
|
||||
role="admin",
|
||||
metadata={"department": "IT"}
|
||||
)
|
||||
"""
|
||||
```
|
||||
|
||||
## Async Functions
|
||||
|
||||
```python
|
||||
async def fetch_user_data(user_id: int, timeout: float = 30.0) -> UserData:
|
||||
"""Fetch user data from the remote service.
|
||||
|
||||
Asynchronously retrieves user information from the external API
|
||||
with automatic retry logic for transient failures.
|
||||
|
||||
Args:
|
||||
user_id: Unique identifier of the user to fetch.
|
||||
timeout: Maximum time in seconds to wait for response.
|
||||
Defaults to 30.0 seconds.
|
||||
|
||||
Returns:
|
||||
UserData object containing the user's information.
|
||||
|
||||
Raises:
|
||||
UserNotFoundError: If no user exists with the given ID.
|
||||
TimeoutError: If the request exceeds the timeout duration.
|
||||
ServiceUnavailableError: If the remote service is unreachable.
|
||||
"""
|
||||
```
|
||||
|
||||
# Generator and Iterator Docstrings
|
||||
|
||||
```python
|
||||
def paginate_results(
|
||||
query: Query,
|
||||
page_size: int = 100,
|
||||
) -> Iterator[list[Record]]:
|
||||
"""Paginate through query results.
|
||||
|
||||
Yields pages of records from the query, handling cursor-based
|
||||
pagination automatically.
|
||||
|
||||
Args:
|
||||
query: Database query to paginate.
|
||||
page_size: Number of records per page. Defaults to 100.
|
||||
|
||||
Yields:
|
||||
Lists of Record objects, each list containing up to page_size items.
|
||||
|
||||
Example:
|
||||
>>> for page in paginate_results(query, page_size=50):
|
||||
... process_records(page)
|
||||
"""
|
||||
```
|
||||
|
||||
# Property Docstrings
|
||||
|
||||
```python
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
"""Return the full name of the user.
|
||||
|
||||
Combines first_name and last_name with proper spacing.
|
||||
Returns an empty string if both names are unset.
|
||||
"""
|
||||
return f"{self.first_name} {self.last_name}".strip()
|
||||
|
||||
|
||||
@property
|
||||
def is_active(self) -> bool:
|
||||
"""Return whether the user account is currently active.
|
||||
|
||||
An account is considered active if it has been verified
|
||||
and has not been suspended or deleted.
|
||||
"""
|
||||
return self.verified and not self.suspended
|
||||
```
|
||||
|
||||
# Context Manager Docstrings
|
||||
|
||||
```python
|
||||
@contextmanager
|
||||
def database_transaction(
|
||||
connection: Connection,
|
||||
isolation_level: str = "READ_COMMITTED",
|
||||
) -> Iterator[Transaction]:
|
||||
"""Context manager for database transactions.
|
||||
|
||||
Provides automatic commit on success and rollback on failure.
|
||||
Nested transactions are supported via savepoints.
|
||||
|
||||
Args:
|
||||
connection: Active database connection.
|
||||
isolation_level: Transaction isolation level.
|
||||
Options: "READ_UNCOMMITTED", "READ_COMMITTED",
|
||||
"REPEATABLE_READ", "SERIALIZABLE".
|
||||
Defaults to "READ_COMMITTED".
|
||||
|
||||
Yields:
|
||||
Transaction object for executing queries.
|
||||
|
||||
Raises:
|
||||
TransactionError: If transaction cannot be started.
|
||||
|
||||
Example:
|
||||
>>> with database_transaction(conn) as tx:
|
||||
... tx.execute("INSERT INTO users VALUES (?)", [user])
|
||||
"""
|
||||
```
|
||||
|
||||
# Dataclass and Pydantic Model Docstrings
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class OrderItem:
|
||||
"""Represents a single item in an order.
|
||||
|
||||
Attributes:
|
||||
product_id: Unique identifier of the product.
|
||||
quantity: Number of units ordered. Must be positive.
|
||||
unit_price: Price per unit in the order currency.
|
||||
discount: Applied discount as a decimal (0.0-1.0).
|
||||
"""
|
||||
|
||||
product_id: str
|
||||
quantity: int
|
||||
unit_price: Decimal
|
||||
discount: Decimal = Decimal("0.0")
|
||||
|
||||
|
||||
class UserSettings(BaseModel):
|
||||
"""User preference settings.
|
||||
|
||||
Stores user-configurable settings for the application,
|
||||
including notification preferences and display options.
|
||||
|
||||
Attributes:
|
||||
theme: UI theme preference. Either "light" or "dark".
|
||||
notifications_enabled: Whether to send notifications.
|
||||
language: Preferred language code (ISO 639-1).
|
||||
timezone: User's timezone (IANA format).
|
||||
"""
|
||||
|
||||
theme: Literal["light", "dark"] = "light"
|
||||
notifications_enabled: bool = True
|
||||
language: str = "en"
|
||||
timezone: str = "UTC"
|
||||
```
|
||||
|
||||
# Enum Docstrings
|
||||
|
||||
```python
|
||||
class OrderStatus(Enum):
|
||||
"""Status values for order lifecycle.
|
||||
|
||||
Attributes:
|
||||
PENDING: Order created but not yet processed.
|
||||
CONFIRMED: Order confirmed and awaiting fulfillment.
|
||||
SHIPPED: Order has been shipped to customer.
|
||||
DELIVERED: Order successfully delivered.
|
||||
CANCELLED: Order was cancelled before fulfillment.
|
||||
"""
|
||||
|
||||
PENDING = "pending"
|
||||
CONFIRMED = "confirmed"
|
||||
SHIPPED = "shipped"
|
||||
DELIVERED = "delivered"
|
||||
CANCELLED = "cancelled"
|
||||
```
|
||||
|
||||
# Exception Docstrings
|
||||
|
||||
```python
|
||||
class ValidationError(Exception):
|
||||
"""Raised when input validation fails.
|
||||
|
||||
This exception is raised when user input or API request data
|
||||
fails validation rules.
|
||||
|
||||
Attributes:
|
||||
field: Name of the field that failed validation.
|
||||
message: Human-readable error message.
|
||||
code: Machine-readable error code for client handling.
|
||||
|
||||
Example:
|
||||
>>> raise ValidationError(
|
||||
... field="email",
|
||||
... message="Invalid email format",
|
||||
... code="INVALID_EMAIL"
|
||||
... )
|
||||
"""
|
||||
|
||||
def __init__(self, field: str, message: str, code: str) -> None:
|
||||
"""Initialize ValidationError.
|
||||
|
||||
Args:
|
||||
field: Name of the field that failed validation.
|
||||
message: Human-readable description of the error.
|
||||
code: Machine-readable error code.
|
||||
"""
|
||||
self.field = field
|
||||
self.message = message
|
||||
self.code = code
|
||||
super().__init__(f"{field}: {message}")
|
||||
```
|
||||
|
||||
# Docstring Sections Reference
|
||||
|
||||
The following sections are supported in Google style docstrings:
|
||||
|
||||
| Section | Purpose |
|
||||
|---------|---------|
|
||||
| `Args:` | Document function/method parameters |
|
||||
| `Returns:` | Describe the return value |
|
||||
| `Yields:` | Describe yielded values (generators) |
|
||||
| `Raises:` | List exceptions that may be raised |
|
||||
| `Attributes:` | Document class/module attributes |
|
||||
| `Example:` / `Examples:` | Provide usage examples |
|
||||
| `Note:` | Additional information or caveats |
|
||||
| `Warning:` | Important warnings for users |
|
||||
| `Todo:` | Future improvements or tasks |
|
||||
| `See Also:` | References to related functions/classes |
|
||||
|
||||
# Best Practices
|
||||
|
||||
1. **Be Concise**: First line should be a brief summary under 79 characters.
|
||||
2. **Use Imperative Mood**: Write "Calculate the sum" not "Calculates the sum".
|
||||
3. **Document All Public APIs**: Every public function, class, and method needs a docstring.
|
||||
4. **Rely on Type Hints**: Avoid duplicating type information in docstrings when type hints are present. Modern documentation tools extract types from annotations.
|
||||
5. **Provide Examples**: Complex functions benefit from usage examples.
|
||||
6. **Document Exceptions**: List all exceptions that can be raised.
|
||||
7. **Keep Updated**: Update docstrings when code changes.
|
||||
8. **Use Proper Indentation**: Continuation lines should be indented consistently.
|
||||
|
||||
# Linting Configuration
|
||||
|
||||
Recommended Ruff configuration for `pyproject.toml`:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["D", "F", "E", "W", "I", "UP", "B", "SIM", "S", "A", "C90", "RET", "PERF"]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
```
|
||||
|
||||
Key rule groups: **D** (docstrings), **F** (pyflakes), **E/W** (pycodestyle),
|
||||
**I** (imports), **UP** (pyupgrade), **B** (bugbear), **SIM** (simplify),
|
||||
**S** (security/bandit), **A** (builtins shadowing), **C90** (complexity),
|
||||
**RET** (return statements), **PERF** (performance).
|
||||
|
||||
# Common Ruff Docstring Rules
|
||||
|
||||
| Rule | Description |
|
||||
|------|-------------|
|
||||
| D100 | Missing docstring in public module |
|
||||
| D101 | Missing docstring in public class |
|
||||
| D102 | Missing docstring in public method |
|
||||
| D103 | Missing docstring in public function |
|
||||
| D107 | Missing docstring in `__init__` |
|
||||
| D200 | One-line docstring should fit on one line |
|
||||
| D205 | 1 blank line required between summary and description |
|
||||
| D400 | First line should end with a period |
|
||||
| D401 | First line should be in imperative mood |
|
||||
| D417 | Missing argument descriptions in docstring |
|
||||
|
||||
Reference in new issue
Block a user