| name | python-code-style |
| description | Use when configuring ruff/mypy/pyright, setting up formatting, writing docstrings, or establishing coding standards. Do NOT use for general patterns (use python-patterns) or testing (use python-testing). |
| paths | **/*.py, **/pyproject.toml, **/setup.cfg, **/.flake8, **/mypy.ini |
Python Code Style & Tooling
ํ๋จ ๊ธฐ์ค๊ณผ ๊ท์น ์ค์ฌ. ruff/mypy ์ค์ ๊ณผ ๋ค์ด๋ฐ ๊ท์น์ ์ฌ๋ฐ๋ฅธ ์ ํ์ ์๋ด.
Quick Start
CRITICAL Rules
- ALWAYS use
ruff as single linter+formatter -- flake8+isort+black ์กฐํฉ ๋์ฒด
- ALWAYS enable strict
mypy or pyright for production code
- ALWAYS 120 char line length -- ํ๋ ๋์คํ๋ ์ด ํ์ค
- ALWAYS absolute imports --
from mypackage.utils import x (relative imports ๊ธ์ง)
- NEVER
from module import * -- ๋ช
์์ import๋ง
- ALWAYS Google-style docstrings -- public API์ ํ์
- PREFER
ruff check --fix + ruff format -- ์๋ ํฌ๋งคํ
๊ธ์ง
- ALWAYS configure
per-file-ignores for tests -- S101(assert), ARG ํ์ฉ
- PREFER
pyproject.toml ๋จ์ผ ์ค์ -- setup.cfg, .flake8 ๋ฑ ๋ถ์ฐ ๊ธ์ง
Type Checker Decision
Need type checking?
+-- New project, full control? --> pyright (strict mode, faster, LSP ๋ด์ฅ)
+-- Existing project, gradual adoption? --> mypy (strict=true, overrides๋ก ์ ์ง์ )
+-- Both available? --> pyright (๊ฐ๋ฐ ์) + mypy (CI์ฉ)
+-- Django/SQLAlchemy project? --> mypy + django-stubs/sqlalchemy-stubs
mypy Config
[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
warn_unused_ignores = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
pyright Config
[tool.pyright]
pythonVersion = "3.12"
typeCheckingMode = "strict"
Ruff Configuration
Strictness Levels
| Level | select | Use Case |
|---|
| Minimal | E, W, F, I, B, UP | ๋ ๊ฑฐ์ ํ๋ก์ ํธ, ๋น ๋ฅธ ๋์
|
| Standard | + C4, SIM, PT, RET, RUF, PERF | ์ ๊ท ํ๋ก์ ํธ ๊ถ์ฅ |
| Strict | ALL (with ignores) | ์ต๋ ์์ ์ฑ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ๋ฐ |
Standard Config (Recommended)
[tool.ruff]
line-length = 120
target-version = "py312"
[tool.ruff.lint]
select = [
"E", "W", "F", "I", "B", "C4",
"UP", "SIM", "PT", "RET", "RUF", "PERF",
]
ignore = ["E501"]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101", "ARG", "PT004"]
"__init__.py" = ["F401"]
[tool.ruff.lint.isort]
known-first-party = ["mypackage"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true
Naming Rules
| Element | Convention | Example |
|---|
| Module/file | snake_case | user_repository.py |
| Class | PascalCase | UserRepository |
| Function/variable | snake_case | get_user_by_email |
| Constant | SCREAMING_SNAKE_CASE | MAX_RETRY_ATTEMPTS |
| Type variable | PascalCase or single letter | T, KeyType |
| Private | _prefix | _internal_cache |
| Acronym in class | Uppercase | HTTPClientFactory |
Naming Anti-Patterns
usr_repo.py, ord_proc.py, http_cli.py
user_repository.py, order_processing.py, http_client.py
data, info, result, temp, foo
user_count, order_total, parsed_response
Docstring Rules
When to Write
| Target | Required? |
|---|
| Public function/method | Yes -- ํญ์ |
| Public class | Yes -- __init__ ํฌํจ |
| Private with complex logic | Yes |
| Trivial getter/setter | No -- ํ์
ํํธ๋ก ์ถฉ๋ถ |
| Test function | No -- ํจ์๋ช
์ด ๋ฌธ์ |
Format (Google Style)
def process_batch(
items: list[Item],
max_workers: int = 4,
on_progress: Callable[[int, int], None] | None = None,
) -> BatchResult:
"""Process items concurrently using a worker pool.
Args:
items: The items to process. Must not be empty.
max_workers: Maximum concurrent workers. Defaults to 4.
on_progress: Optional callback receiving (completed, total).
Returns:
BatchResult with succeeded items and failures.
Raises:
ValueError: If items is empty.
"""
Rules:
- ํ ์ค์ด๋ฉด
"""Retrieve a user by ID.""" (closing quotes ๊ฐ์ ์ค)
- ๋ณต์กํ๋ฉด summary line + blank line + details
Args, Returns, Raises ์์ ๊ณ ์
Example: ์น์
์ doctest๋ก ์คํ ๊ฐ๋ฅํ๊ฒ
Import Organization
import os
from collections.abc import Callable
import httpx
from pydantic import BaseModel
from myproject.models import User
from myproject.services import UserService
ruff์ I rule์ด ์๋ ์ ๋ ฌ. known-first-party ์ค์ ํ์.
Anti-Patterns
| Anti-Pattern | Problem | Fix |
|---|
from module import * | ๋ค์์คํ์ด์ค ์ค์ผ | ๋ช
์์ import |
from ..utils import x | ๋ฆฌํฉํ ๋ง ์ ๊นจ์ง | ์ ๋ import |
print() for debugging | ํ๋ก๋์
์ ๋จ์ | logging ๋๋ ruff T20 rule |
| Manual formatting | ์ผ๊ด์ฑ ๊นจ์ง, ์๊ฐ ๋ญ๋น | ruff format ์๋ํ |
| setup.cfg + .flake8 + .isort.cfg | ์ค์ ํ์ผ ๋ถ์ฐ | pyproject.toml ๋จ์ผ ํ์ผ |
# type: ignore without code | ์ด๋ค ์๋ฌ์ธ์ง ๋ถ๋ช
ํ | # type: ignore[specific-error] |
noqa without code | ์ด๋ค ๋ฃฐ์ธ์ง ๋ถ๋ช
ํ | # noqa: E501 ๋ช
์ |
Gotchas
- โ ruff์์
select = ["ALL"] โ ๋๋ฌด ๊ณต๊ฒฉ์ , ["E", "F", "W", "I"]๋ถํฐ ์์
- โ mypy
strict ๋ชจ๋ ๊ธฐ์กด ํ๋ก์ ํธ์ ๋ฐ๋ก ์ ์ฉ โ ์ ์ง์ ์ ์ฉ ํ์
- โ docstring์ ํ๋ผ๋ฏธํฐ ํ์
์ค๋ณต ๊ธฐ์ โ type hints๊ฐ ์์ผ๋ฉด docstring์์ ํ์
์๋ต
Troubleshooting
| Symptom | Cause | Solution |
|---|
| ruff format๊ณผ lint ์ถฉ๋ | COM812, ISC001 rule ํ์ฑ | ignore์ ์ถ๊ฐ |
| mypy "missing stubs" | ์๋ํํฐ ํ์
์คํ
์์ | pip install types-xxx ๋๋ [[tool.mypy.overrides]] |
| CI์์๋ง lint ์คํจ | ๋ก์ปฌ ruff ๋ฒ์ ์ฐจ์ด | pre-commit ๋๋ CI์์ ๋ฒ์ ๊ณ ์ |
| isort ์ ๋ ฌ ์ ๋ง์ | known-first-party ๋ฏธ์ค์ | ruff.lint.isort์ ํจํค์ง๋ช
์ถ๊ฐ |
Cross-References
| Topic | Skill |
|---|
| Python ํจํด, ํ์
ํํธ, ๋์์ฑ | python-patterns |
| pytest, TDD, fixtures, mocking | python-testing |
| Ruff ๋ฃฐ ์นดํ
๊ณ ๋ฆฌ ์ ์ฒด, pre-commit ์ค์ | references/ruff-rules-guide.md |
References