소스 정보
- 저장소
- irahardianto/awesome-agv
- 최근 소스 활동
- 2026년 8월 7일 01:34
- 감지된 SKILL.md 언어
- 영어
- 스타
- 150
- 포크
- 48
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/irahardianto/awesome-agv --skill python-idioms명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | python-idioms |
| description | Python type hints, Protocols, Pydantic, async/await, pytest, ruff, mypy strict. |
| paths | ["**/*.py","**/pyproject.toml","**/requirements*.txt"] |
Python rewards explicitness and readability over cleverness. Follow the Zen of Python. If it reads like plain English, it's probably idiomatic.
Scope: This skill covers Python-specific coding idioms. For file layout see
references/project-structure.md. For safety/SAST/performance patterns seereferences/python-patterns-and-anti-patterns.md. For logging seelogging-implementationskill. For quality commands seecode-idioms-and-conventionsrule.
pyproject.toml or *.py files, this skill does not applydjango in dependencies), co-load django-idioms skill alongside this one| Situation | Reference to Load |
|---|---|
| Starting a new project or setting up file layout | references/project-structure.md |
Choosing packages, pyproject.toml setup, or ruff config | references/recommended-dependencies.md |
| Writing code that handles user input, async operations, or I/O | references/python-patterns-and-anti-patterns.md |
from __future__ import annotations), template strings (PEP 750)type X = ... (PEP 695), @override decorator, improved f-strings, itertools.batchedStrEnum, ExceptionGroup + except*, asyncio.TaskGroup, tomllib, fine-grained error locationsmatch/case), X | Y union syntax, TypeAliasType hints are required for all public APIs, class attributes, and function signatures.
list, dict, set) for typing, not typing.List etc.X | Y instead of Union[X, Y] or Optional[X].type Vector[T] = list[T]@override (3.12+) to ensure methods actually override a base class method.TypeVar with constraints and bounds when necessary.Never for functions that always raise an exception or never return.# ❌ Anti-pattern: Untyped or legacy typing
from typing import List, Optional, TypeVar
T = TypeVar('T')
def process_items(items: List[T], strict: Optional[bool] = None) -> List[T]: ...
class Worker(BaseWorker):
def run(self): ... # Overrides base class? Maybe.
# ✅ Recommended pattern: Modern typing syntax
from typing import override, Never
type Vector[T] = list[T]
def process_items[T](items: Vector[T], strict: bool | None = None) -> Vector[T]: ...
class Worker(BaseWorker):
@override
def run(self) -> None: ...
def crash_and_burn(msg: str) -> Never:
raise RuntimeError(msg)
Protocols for Structural Subtyping
Define required behavior via Protocol instead of inheritance when depending on abstractions.
TypedDict for JSON/Dict payloads
When dealing with dictionaries that have a fixed schema, use TypedDict.
Exception.AppError base class).except Exception: pass). Use contextlib.suppress() if appropriate and intentional.except* (3.11+) when multiple errors can occur simultaneously.add_note() (3.11+) to attach additional context to exceptions before re-raising.None (DeepSource bug risk).finally blocks should not swallow exceptions; they are for cleanup only.# ❌ Anti-pattern: Broad except, swallowing errors, assigning None
def load_data():
try:
data = fetch()
return data
except Exception as e:
print(f"Failed: {e}")
finally:
return None # Swallows exception!
res = dict.get("key") # Might return None, then what?
# ✅ Recommended pattern: Specific exceptions, exception groups, add_note
class AppError(Exception): pass
class NetworkError(AppError): pass
def load_data() -> dict:
try:
return fetch()
except TimeoutError as e:
e.add_note("Timeout while fetching external data")
raise NetworkError("Failed to fetch") from e
# Exception groups (3.11+)
try:
raise ExceptionGroup("Multiple failures", [NetworkError(), ValueError()])
except* NetworkError as e:
handle_network(e)
except* ValueError as e:
handle_value(e)
@dataclass for internal data structures.@dataclass(frozen=True, slots=True) (3.10+) as the recommended default for value objects. slots=True avoids __dict__ creation, saving memory and speeding up attribute access.@dataclass(kw_only=True) (3.10+) to require keyword arguments.BaseModel when data crosses system boundaries (I/O, APIs, config) and requires validation.model_validator and field_validator for complex validation rules.# ✅ Recommended pattern: Dataclasses
from dataclasses import dataclass
@dataclass(frozen=True, slots=True, kw_only=True)
class UserConfig:
id: int
username: str
active: bool = True
# ✅ Recommended pattern: Pydantic Validation
from pydantic import BaseModel, field_validator, model_validator
class User(BaseModel):
password: str
password_confirm: str
@model_validator(mode="after")
def check_passwords_match(self) -> "User":
if self.password != self.password_confirm:
raise ValueError("Passwords do not match")
return self
When to use which:
| Use Case | Recommendation |
|---|---|
| Untrusted / external data (API input, config files, webhook payloads) | pydantic.BaseModel |
| Internal value objects, domain entities (no validation needed) | @dataclass(frozen=True, slots=True) |
| Dictionary-shaped typed data (JSON response shapes, kwargs mappings) | TypedDict |
| Named string or integer constants | enum.StrEnum / enum.IntEnum |
Prefer composition and dependency injection over deep inheritance hierarchies. Depend on typing.Protocol to define the interface a function or class expects.
# ✅ Recommended pattern: Dependency Injection with Protocols
from typing import Protocol
class MessageSender(Protocol):
def send(self, msg: str) -> None: ...
class EmailSender:
def send(self, msg: str) -> None:
pass # Implementation
def notify_user(sender: MessageSender) -> None:
sender.send("Hello")
asyncio.TaskGroup (3.11+) as the preferred way to run concurrent tasks over asyncio.gather. It provides structured concurrency and better error handling.asyncio.Runner (3.11+) for managing the event loop lifecycle instead of raw get_event_loop().asyncio.run() from inside an already running event loop.asyncio.to_thread() to offload blocking/CPU-bound work to a thread pool so the event loop is not blocked.# ❌ Anti-pattern: Unstructured concurrency
import asyncio
async def main():
await asyncio.gather(task1(), task2()) # Errors in one task don't cancel the other easily
# ✅ Recommended pattern: Structured concurrency with TaskGroup
import asyncio
async def main():
try:
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(fetch_data())
task2 = tg.create_task(process_data())
# tg automatically waits for all tasks. If one fails, others are cancelled.
except* Exception as e:
print(f"Task group failed: {e}")
| Entity | Convention | Example |
|---|---|---|
| Variables, Functions, Methods | snake_case | calculate_total() |
| Classes, Protocols, TypeAliases | PascalCase | UserRepository |
| Constants | UPPER_SNAKE_CASE | MAX_RETRIES |
| Protected/Private members | _leading_underscore | _internal_cache |
| Dunder methods | __dunder__ | __init__ |
fetch_user_by_id(user_id: int) is better than get_u(i).with statements for resource management (files, network connections, locks).yield for lazy evaluation and memory efficiency when dealing with large sequences.dataclasses.replace: Use for immutable updates to dataclasses.functools.cache / lru_cache: Use for memoizing expensive deterministic function calls.__slots__: Use via @dataclass(slots=True) or explicitly to save memory on heavily instantiated classes.StrEnum: (3.11+) Use for string-based enumerations.match/case for structural pattern matching instead of long if/elif/else chains.str.removeprefix() and str.removesuffix() instead of error-prone slicing or strip().dict1 | dict2 to merge dictionaries.:=: Use for assignment expressions to avoid repeating expensive calls or improving loop conditions.itertools.batched (3.12+): Use to cleanly chunk iterables into batches.pathlib.Path: ALWAYS prefer over os.path for file operations.[], {}). Use None as a sentinel. (DeepSource #1 bug risk)# ❌ Anti-pattern: Mutable default argument
def add_item(item: str, items: list = []) -> list:
items.append(item)
return items
# ✅ Recommended pattern: None sentinel
def add_item(item: str, items: list | None = None) -> list:
if items is None:
items = []
items.append(item)
return items
# ✅ Recommended pattern: Pattern matching & itertools.batched
import itertools
def process(command: dict | list):
match command:
case {"action": "delete", "id": int(id_val)}:
delete_record(id_val)
case list(items):
for batch in itertools.batched(items, 100):
process_batch(batch)
Write deterministic tests focusing on behavior.
pytest --cov=src --cov-report=term-missing@pytest.mark.parametrize for data-driven testing.pytest-asyncio for async tests.patch decorators when possible.Test Double Selection Table:
| Approach | When to Use |
|---|---|
| Hand-written fake (implement Protocol) | Simple interface, few methods, need stateful behavior |
pytest-mock (mocker fixture) | Verify call counts, argument matching |
respx | HTTP boundary mocking — intercepts httpx calls |
@pytest.mark.parametrize | Same logic, multiple input/output pairs |
Snapshot (syrupy) | Large outputs — JSON responses, CLI output |
hypothesis | Property-based testing for wide input spaces |
NEVER suppress these — they signal structural problems:
| Rule | What It Signals | What To Do Instead |
|---|---|---|
F841 (unused variable) | Dead code | Remove the variable |
S rules (security) | Security vulnerability | Fix the vulnerability |
B006 (mutable default) | Shared mutable state bug | Use None sentinel pattern |
ANN (missing annotations) | Untyped public API | Add type annotations |
E712 (== True/False/None) | Identity vs equality confusion | Use is / is not |
Acceptable suppressions (with mandatory # noqa: + reason comment):
| Rule | When Acceptable |
|---|---|
S101 (assert) | In test files only |
ANN101/ANN102 (self/cls annotations) | Standard convention — self/cls never need annotations |
T20 (print) | In CLI tools or scripts |
ARG (unused argument) | In interface implementations where signature is fixed |
Rule of thumb: If you're about to write # noqa:, stop and ask: "Am I suppressing a real design problem?"
Adopt the standard Rust/TS-style static analysis workflow:
| Phase | Command | Purpose |
|---|---|---|
| TDD / rapid iteration | mypy src/ --strict | Type-check only — fastest feedback |
| Pre-commit | ruff check . --fix | Lint — must pass with zero warnings |
| Pre-commit | ruff format . | Formatting — non-negotiable |
| Pre-commit | pytest | Unit tests — must all pass |
| Coverage verification | pytest --cov=src --cov-report=term-missing | Verify before merging |
| Security audit | bandit -r src/ -c pyproject.toml | Security scanning |
| Dependency audit | pip-audit | CVE scanning |
Configure all tools in pyproject.toml — never use per-file pragma comments to disable checks without a # noqa: reason comment.
Never use print() in production. Always use a configured logger (see logging-implementation skill).
Document all public items:
# ❌ Anti-pattern: Undocumented public API
def calculate_discount(price: float, rate: float) -> float:
return price * (1 - rate)
# ✅ Recommended pattern: Documented public API
def calculate_discount(price: float, rate: float) -> float:
"""Calculates the final price after applying a discount rate.
Args:
price: The original price.
rate: The discount rate as a decimal (e.g., 0.2 for 20%).
Returns:
The final discounted price.
Raises:
ValueError: If the rate is not between 0.0 and 1.0.
"""
if not (0.0 <= rate <= 1.0):
raise ValueError("Rate must be between 0.0 and 1.0")
return price * (1 - rate)
pip-audit in CI.pyproject.toml as the single source of truth for project metadata.uv.lock, requirements.lock).For the full curated dependency list with versions, see
references/recommended-dependencies.md.
os.environ / os.getenv() calls throughout the codebase.BaseSettings for validated, typed config.# ❌ Anti-pattern: Scattered os.getenv calls
import os
def connect_db():
db_url = os.getenv("DATABASE_URL") # Fails later if missing
# connect...
# ✅ Recommended pattern: Centralized typed config
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
api_key: str
# Fails immediately at startup if env vars are missing or invalid
settings = Settings()
def connect_db():
db_url = settings.database_url
# connect...
eval() or exec() with untrusted input.pickle on untrusted data.references/python-patterns-and-anti-patterns.md for the full catalog of safety and security patterns.perf-optimization skill for profiling and performance guidance.@code-idioms-and-conventions.md@references/project-structure.md@security-principles.md@architectural-pattern.md@testing-strategy.md@error-handling-principles.md@core-design-principles.md@logging-and-observability-mandate.md@.agents/skills/logging-implementation/SKILL.md@.agents/skills/django-idioms/SKILL.md@.agents/skills/testability-patterns/SKILL.md@concurrency-and-threading-principles.md@performance-optimization-principles.md@resources-and-memory-management-principles.md@security-mandate.md@dependency-management-principles.md@references/recommended-dependencies.md@references/python-patterns-and-anti-patterns.md