python-skills 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_skills/__init__.py +10 -0
- python_skills/__main__.py +6 -0
- python_skills/adapters/__init__.py +48 -0
- python_skills/adapters/agent_skills.py +415 -0
- python_skills/adapters/aider_adapter.py +226 -0
- python_skills/adapters/base.py +153 -0
- python_skills/adapters/claude.py +474 -0
- python_skills/adapters/cline.py +332 -0
- python_skills/adapters/codex.py +24 -0
- python_skills/adapters/continue_adapter.py +198 -0
- python_skills/adapters/cursor.py +327 -0
- python_skills/adapters/gemini.py +26 -0
- python_skills/adapters/goose.py +26 -0
- python_skills/adapters/junie.py +25 -0
- python_skills/adapters/kiro.py +382 -0
- python_skills/adapters/opencode.py +27 -0
- python_skills/adapters/roo.py +25 -0
- python_skills/adapters/universal.py +203 -0
- python_skills/adapters/vscode.py +27 -0
- python_skills/adapters/windsurf.py +26 -0
- python_skills/adapters/zed.py +27 -0
- python_skills/cli.py +326 -0
- python_skills/config.py +160 -0
- python_skills/detector.py +152 -0
- python_skills/installer.py +163 -0
- python_skills/markers.py +115 -0
- python_skills/skills/__init__.py +14 -0
- python_skills/skills/loader.py +171 -0
- python_skills/skills/metadata.py +152 -0
- python_skills/skills/registry.py +101 -0
- python_skills/state.py +204 -0
- python_skills-1.0.0.dist-info/METADATA +99 -0
- python_skills-1.0.0.dist-info/RECORD +105 -0
- python_skills-1.0.0.dist-info/WHEEL +4 -0
- python_skills-1.0.0.dist-info/entry_points.txt +2 -0
- python_skills-1.0.0.dist-info/licenses/LICENSE +21 -0
- skills/advanced_python.md +239 -0
- skills/anti_patterns/index.md +406 -0
- skills/comprehensions.md +167 -0
- skills/control_flow.md +175 -0
- skills/data_structures.md +243 -0
- skills/debugging/common_bugs.md +222 -0
- skills/debugging/inspection_techniques.md +249 -0
- skills/debugging/root_cause.md +203 -0
- skills/engineering/application_logging.md +195 -0
- skills/engineering/cli_apps.md +207 -0
- skills/engineering/configuration.md +218 -0
- skills/engineering/database.md +240 -0
- skills/engineering/dependency_management.md +205 -0
- skills/engineering/http_clients.md +267 -0
- skills/engineering/modules_packages.md +211 -0
- skills/engineering/packaging.md +197 -0
- skills/engineering/project_structure.md +155 -0
- skills/engineering/pyproject_toml.md +302 -0
- skills/engineering/virtual_environments.md +206 -0
- skills/functions.md +244 -0
- skills/generation/async_concurrency.md +291 -0
- skills/generation/error_handling.md +276 -0
- skills/generation/protocols_generics.md +243 -0
- skills/generation/type_hints.md +290 -0
- skills/generation/validation_pipeline.md +274 -0
- skills/generation/workflow.md +190 -0
- skills/oop.md +228 -0
- skills/quality/abstractions.md +154 -0
- skills/quality/comments.md +177 -0
- skills/quality/documentation.md +176 -0
- skills/quality/duplication.md +137 -0
- skills/quality/maintainability.md +142 -0
- skills/quality/naming.md +171 -0
- skills/quality/quality_functions.md +245 -0
- skills/quality/readability.md +239 -0
- skills/quality/type_annotations.md +192 -0
- skills/refactoring/behavior_preservation.md +157 -0
- skills/refactoring/incremental.md +187 -0
- skills/refactoring/interface_stability.md +199 -0
- skills/refactoring/safe_refactoring.md +206 -0
- skills/security/auth_boundaries.md +200 -0
- skills/security/command_injection.md +207 -0
- skills/security/dependency_risks.md +282 -0
- skills/security/file_handling.md +156 -0
- skills/security/input_validation.md +190 -0
- skills/security/path_traversal.md +172 -0
- skills/security/secrets.md +171 -0
- skills/security/sql_injection.md +188 -0
- skills/security/unsafe_deserialization.md +164 -0
- skills/stdlib/argparse.md +178 -0
- skills/stdlib/collections.md +212 -0
- skills/stdlib/datetime.md +187 -0
- skills/stdlib/functools.md +238 -0
- skills/stdlib/itertools.md +183 -0
- skills/stdlib/json.md +162 -0
- skills/stdlib/logging.md +185 -0
- skills/stdlib/os_sys.md +184 -0
- skills/stdlib/pathlib.md +218 -0
- skills/stdlib/re.md +171 -0
- skills/stdlib/statistics.md +112 -0
- skills/stdlib/subprocess.md +211 -0
- skills/testing/async_tests.md +249 -0
- skills/testing/coverage.md +168 -0
- skills/testing/edge_cases.md +197 -0
- skills/testing/fixtures_mocks.md +203 -0
- skills/testing/organization.md +205 -0
- skills/testing/parameterized.md +174 -0
- skills/testing/regression_tests.md +165 -0
- skills/variables_types.md +107 -0
skills/quality/naming.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Quality: Naming
|
|
2
|
+
|
|
3
|
+
**Purpose**: Meaningful, consistent naming conventions.
|
|
4
|
+
|
|
5
|
+
**When to use**: All code generation. Names are the first documentation.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Rules
|
|
10
|
+
|
|
11
|
+
### Naming Conventions (PEP 8)
|
|
12
|
+
| Type | Convention | Example |
|
|
13
|
+
|------|------------|---------|
|
|
14
|
+
| Module | lowercase, short | `utils`, `http_client` |
|
|
15
|
+
| Package | lowercase, short | `mypackage`, `api_client` |
|
|
16
|
+
| Class | PascalCase | `UserService`, `HTTPClient` |
|
|
17
|
+
| Function | snake_case | `get_user`, `process_order` |
|
|
18
|
+
| Method | snake_case | `save`, `validate` |
|
|
19
|
+
| Constant | UPPER_SNAKE_CASE | `MAX_RETRIES`, `DEFAULT_TIMEOUT` |
|
|
20
|
+
| Variable | snake_case | `user_count`, `is_active` |
|
|
21
|
+
| Type variable | PascalCase | `T`, `UserT`, `KeyT` |
|
|
22
|
+
| Exception | PascalCase + Error/Exception | `ValidationError`, `NotFoundError` |
|
|
23
|
+
|
|
24
|
+
### Descriptive Names
|
|
25
|
+
```python
|
|
26
|
+
# GOOD
|
|
27
|
+
user_count = len(users)
|
|
28
|
+
max_retry_attempts = 3
|
|
29
|
+
is_user_active = user.status == "active"
|
|
30
|
+
process_payment(payment)
|
|
31
|
+
|
|
32
|
+
# BAD
|
|
33
|
+
n = len(users)
|
|
34
|
+
m = 3
|
|
35
|
+
flag = user.status == "active"
|
|
36
|
+
proc(p)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Boolean Names
|
|
40
|
+
```python
|
|
41
|
+
# GOOD — positive, question form
|
|
42
|
+
is_active = True
|
|
43
|
+
has_permission = False
|
|
44
|
+
can_edit = True
|
|
45
|
+
should_retry = True
|
|
46
|
+
|
|
47
|
+
# BAD
|
|
48
|
+
not_inactive = True
|
|
49
|
+
no_permission = False
|
|
50
|
+
active = True # Ambiguous: noun or adj?
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Collection Names
|
|
54
|
+
```python
|
|
55
|
+
# GOOD — plural for collections
|
|
56
|
+
users = get_users()
|
|
57
|
+
active_users = [u for u in users if u.is_active]
|
|
58
|
+
user_by_id = {u.id: u for u in users}
|
|
59
|
+
|
|
60
|
+
# BAD
|
|
61
|
+
user_list = get_users()
|
|
62
|
+
user_dict = {u.id: u for u in users}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Function Names
|
|
66
|
+
```python
|
|
67
|
+
# GOOD — verb phrase
|
|
68
|
+
get_user(user_id)
|
|
69
|
+
create_user(data)
|
|
70
|
+
validate_email(email)
|
|
71
|
+
calculate_total(items)
|
|
72
|
+
save_to_database(record)
|
|
73
|
+
|
|
74
|
+
# BAD
|
|
75
|
+
user(user_id) # Noun
|
|
76
|
+
user_create(data) # Verb-noun reversed
|
|
77
|
+
check(email) # Vague
|
|
78
|
+
total(items) # Noun
|
|
79
|
+
persist(record) # Too generic
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Class Names
|
|
83
|
+
```python
|
|
84
|
+
# GOOD — noun phrase
|
|
85
|
+
class UserService:
|
|
86
|
+
class PaymentProcessor:
|
|
87
|
+
class DatabaseConnection:
|
|
88
|
+
class HTTPClient:
|
|
89
|
+
class ValidationError:
|
|
90
|
+
|
|
91
|
+
# BAD
|
|
92
|
+
class UserManager: # "Manager" is vague
|
|
93
|
+
class Utils: # Namespace, not class
|
|
94
|
+
class Data: # Too generic
|
|
95
|
+
class HandleUser: # Verb phrase
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Module Names
|
|
99
|
+
```python
|
|
100
|
+
# GOOD
|
|
101
|
+
http_client.py
|
|
102
|
+
user_service.py
|
|
103
|
+
payment_processor.py
|
|
104
|
+
|
|
105
|
+
# BAD
|
|
106
|
+
client.py # Too generic
|
|
107
|
+
user.py # Conflicts with class User
|
|
108
|
+
service.py # Vague
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Decision Rules
|
|
114
|
+
|
|
115
|
+
| Context | Convention |
|
|
116
|
+
|---------|------------|
|
|
117
|
+
| Public API | Full descriptive names |
|
|
118
|
+
| Internal helper | Can be shorter if context clear |
|
|
119
|
+
| Loop variable | `i`, `j`, `k` or `idx` |
|
|
120
|
+
| Comprehension | `x`, `item`, `elem` |
|
|
121
|
+
| Type variable | `T`, `K`, `V`, `T_co` |
|
|
122
|
+
| Private | Leading `_` (`_internal`) |
|
|
123
|
+
| Dunder | `__special__` (Python reserved) |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Preferred Patterns
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# Consistent prefixes for related functions
|
|
131
|
+
def fetch_user(user_id: int) -> User:
|
|
132
|
+
def fetch_users(filters: UserFilters) -> list[User]:
|
|
133
|
+
def fetch_user_by_email(email: str) -> User | None:
|
|
134
|
+
|
|
135
|
+
# Consistent suffixes for related classes
|
|
136
|
+
class UserRepository:
|
|
137
|
+
class OrderRepository:
|
|
138
|
+
class ProductRepository:
|
|
139
|
+
|
|
140
|
+
# Clear boolean naming
|
|
141
|
+
if user.can_access(resource):
|
|
142
|
+
if config.should_retry_on_failure:
|
|
143
|
+
if not order.is_cancelled:
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Avoid
|
|
149
|
+
|
|
150
|
+
- Single letters (except loops/math)
|
|
151
|
+
- Abbreviations (`cfg`, `msg`, `usr`, `tmp`)
|
|
152
|
+
- Hungarian notation (`str_name`, `int_count`)
|
|
153
|
+
- Redundant prefixes (`my_`, `the_`, `obj_`)
|
|
154
|
+
- Name shadowing (`list = [...]`, `dict = {}`)
|
|
155
|
+
- Similar names differing only by case (`user` vs `User`)
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Validation Considerations
|
|
160
|
+
|
|
161
|
+
- Linter naming checks (`ruff` N800 series)
|
|
162
|
+
- `pylint` naming conventions
|
|
163
|
+
- Code review for clarity
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Related Skills
|
|
168
|
+
|
|
169
|
+
- `quality/readability.md`
|
|
170
|
+
- `quality/functions.md`
|
|
171
|
+
- `core/variables_types.md`
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Quality: Functions
|
|
2
|
+
|
|
3
|
+
**Purpose**: Function design principles for maintainable code.
|
|
4
|
+
|
|
5
|
+
**When to use**: Writing or reviewing functions.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
---
|
|
9
|
+
name: quality_functions
|
|
10
|
+
purpose: Function design principles for maintainable code
|
|
11
|
+
category: quality
|
|
12
|
+
triggers:
|
|
13
|
+
- function
|
|
14
|
+
- method
|
|
15
|
+
- parameter
|
|
16
|
+
- signature
|
|
17
|
+
- complexity
|
|
18
|
+
- cyclomatic
|
|
19
|
+
dependencies:
|
|
20
|
+
- quality/readability.md
|
|
21
|
+
- quality/maintainability.md
|
|
22
|
+
- quality/abstractions.md
|
|
23
|
+
- generation/error_handling.md
|
|
24
|
+
- testing/organization.md
|
|
25
|
+
priority: primary
|
|
26
|
+
estimated_tokens: 1500
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Core Rules
|
|
30
|
+
|
|
31
|
+
### Size
|
|
32
|
+
- **Target**: < 30 lines (excluding docstrings)
|
|
33
|
+
- **Maximum**: 50 lines
|
|
34
|
+
- Split if exceeded
|
|
35
|
+
|
|
36
|
+
### Single Responsibility
|
|
37
|
+
- One logical operation per function
|
|
38
|
+
- Name describes what it does
|
|
39
|
+
- If "and" in name → split
|
|
40
|
+
|
|
41
|
+
### Parameters
|
|
42
|
+
- **Target**: ≤ 4 parameters
|
|
43
|
+
- **Maximum**: 7 parameters
|
|
44
|
+
- Use config object for more
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
# BAD — too many params
|
|
48
|
+
def process(a, b, c, d, e, f, g, h):
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
# GOOD — config object
|
|
52
|
+
@dataclass
|
|
53
|
+
class ProcessConfig:
|
|
54
|
+
a: int
|
|
55
|
+
b: str
|
|
56
|
+
c: float
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
def process(config: ProcessConfig):
|
|
60
|
+
...
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Return Values
|
|
64
|
+
- Single return type (union if needed)
|
|
65
|
+
- Early returns for guard clauses
|
|
66
|
+
- Explicit `return` at end (or implicit `None`)
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
# GOOD — early returns
|
|
70
|
+
def find_user(users: list[User], id: int) -> User | None:
|
|
71
|
+
if not users:
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
for user in users:
|
|
75
|
+
if user.id == id:
|
|
76
|
+
return user
|
|
77
|
+
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
# BAD — nested
|
|
81
|
+
def find_user(users: list[User], id: int) -> User | None:
|
|
82
|
+
result = None
|
|
83
|
+
if users:
|
|
84
|
+
for user in users:
|
|
85
|
+
if user.id == id:
|
|
86
|
+
result = user
|
|
87
|
+
break
|
|
88
|
+
return result
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Side Effects
|
|
92
|
+
- Document side effects in docstring
|
|
93
|
+
- Prefer pure functions (same input → same output)
|
|
94
|
+
- Separate pure logic from I/O
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# Pure
|
|
98
|
+
def calculate_total(items: list[Item]) -> Decimal:
|
|
99
|
+
return sum(item.price for item in items)
|
|
100
|
+
|
|
101
|
+
# Impure (I/O) — separate
|
|
102
|
+
def save_order(order: Order) -> Order:
|
|
103
|
+
db.save(order)
|
|
104
|
+
return order
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Function Responsibility
|
|
108
|
+
- One logical operation per function
|
|
109
|
+
- Name describes what it does (verb + object: `validate_email`, `fetch_user`)
|
|
110
|
+
- If name contains "and" → split
|
|
111
|
+
- Each function should be testable in isolation
|
|
112
|
+
|
|
113
|
+
### Parameter Design
|
|
114
|
+
- **Positional-only** (`/`) for `self`, `cls`, or API stability
|
|
115
|
+
- **Keyword-only** (`*`) for boolean flags and optional config
|
|
116
|
+
- **Default values** must be immutable (use `None` sentinel)
|
|
117
|
+
- **Type hints** required for public API, optional for private helpers
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
# GOOD — clear signature
|
|
121
|
+
def fetch_users(
|
|
122
|
+
client: APIClient,
|
|
123
|
+
filters: UserFilters | None = None,
|
|
124
|
+
limit: int = 100,
|
|
125
|
+
*,
|
|
126
|
+
include_inactive: bool = False,
|
|
127
|
+
) -> list[User]:
|
|
128
|
+
...
|
|
129
|
+
|
|
130
|
+
# BAD — ambiguous
|
|
131
|
+
def fetch_users(client, filters=None, limit=100, include_inactive=False):
|
|
132
|
+
...
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Return Value Design
|
|
136
|
+
- Single return type (union if needed for errors)
|
|
137
|
+
- Early returns for guard clauses
|
|
138
|
+
- Explicit `return` at end (or implicit `None`)
|
|
139
|
+
- Use `Result` pattern for operations that can fail
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# GOOD — early returns, single type
|
|
143
|
+
def find_user(users: list[User], id: int) -> User | None:
|
|
144
|
+
if not users:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
for user in users:
|
|
148
|
+
if user.id == id:
|
|
149
|
+
return user
|
|
150
|
+
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
# GOOD — Result pattern for fallible operations
|
|
154
|
+
def parse_user(data: dict) -> Result[User, ValidationError]:
|
|
155
|
+
try:
|
|
156
|
+
return Result.ok(User.model_validate(data))
|
|
157
|
+
except ValidationError as e:
|
|
158
|
+
return Result.err(e)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Side Effects
|
|
162
|
+
- Document side effects in docstring (`"""Creates user and sends welcome email."""`)
|
|
163
|
+
- Prefer pure functions (same input → same output, no external mutation)
|
|
164
|
+
- Separate pure logic from I/O
|
|
165
|
+
- Pass dependencies explicitly, not via global state
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# Pure
|
|
169
|
+
def calculate_total(items: list[Item]) -> Decimal:
|
|
170
|
+
return sum(item.price for item in items)
|
|
171
|
+
|
|
172
|
+
# Impure (I/O) — separate, inject dependencies
|
|
173
|
+
def save_order(order: Order, repo: OrderRepository) -> Order:
|
|
174
|
+
repo.save(order)
|
|
175
|
+
return order
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Decision Rules
|
|
181
|
+
|
|
182
|
+
| Situation | Pattern |
|
|
183
|
+
|-----------|---------|
|
|
184
|
+
| Repeated logic | Extract function |
|
|
185
|
+
| Complex condition | Extract predicate function |
|
|
186
|
+
| Multiple returns | Early returns |
|
|
187
|
+
| Many parameters | Config dataclass |
|
|
188
|
+
| Logic + I/O | Separate functions |
|
|
189
|
+
| Boolean flag controls behavior | Split into two functions |
|
|
190
|
+
| Output parameter (modify argument) | Return new value instead |
|
|
191
|
+
| Recursive without clear base case | Use iteration |
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Preferred Patterns
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
# Composed from small functions
|
|
199
|
+
def process_order(order: Order) -> Result[Fulfillment, OrderError]:
|
|
200
|
+
validate_order(order)
|
|
201
|
+
payment = charge_payment(order)
|
|
202
|
+
fulfillment = create_fulfillment(order)
|
|
203
|
+
notify_customer(order, fulfillment)
|
|
204
|
+
return Result.ok(fulfillment)
|
|
205
|
+
|
|
206
|
+
# Each small, testable
|
|
207
|
+
def validate_order(order: Order) -> None:
|
|
208
|
+
if not order.items:
|
|
209
|
+
raise ValidationError("items", "Order must have items")
|
|
210
|
+
if order.total <= 0:
|
|
211
|
+
raise ValidationError("total", "Total must be positive")
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Avoid
|
|
217
|
+
|
|
218
|
+
- Functions doing 3+ distinct things
|
|
219
|
+
- Boolean parameters controlling behavior (`process(data, True, False)`)
|
|
220
|
+
- Output parameters (modify argument)
|
|
221
|
+
- Global state modification
|
|
222
|
+
- Recursive functions without clear base case (use iteration)
|
|
223
|
+
- `*args`/`**kwargs` without documentation of expected keys
|
|
224
|
+
- Modifying `**kwargs` in place (copy first)
|
|
225
|
+
- Deeply nested functions (limit closure depth)
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Validation Considerations
|
|
230
|
+
|
|
231
|
+
- Cyclomatic complexity per function (< 10)
|
|
232
|
+
- Function length (lines) (< 50)
|
|
233
|
+
- Parameter count (≤ 7)
|
|
234
|
+
- Test coverage per function
|
|
235
|
+
- `ruff` rules: B006 (mutable default), B007 (loop var in closure), B008 (func call in default)
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Related Skills
|
|
240
|
+
|
|
241
|
+
- `quality/readability.md`
|
|
242
|
+
- `quality/maintainability.md`
|
|
243
|
+
- `quality/abstractions.md`
|
|
244
|
+
- `generation/error_handling.md`
|
|
245
|
+
- `testing/organization.md`
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Quality: Readability
|
|
2
|
+
|
|
3
|
+
**Purpose**: Code should be easy for another developer to understand.
|
|
4
|
+
|
|
5
|
+
**When to use**: All code generation and review.
|
|
6
|
+
---
|
|
7
|
+
---
|
|
8
|
+
name: quality_readability
|
|
9
|
+
purpose: Code should be easy for another developer to understand
|
|
10
|
+
category: quality
|
|
11
|
+
triggers:
|
|
12
|
+
- readability
|
|
13
|
+
- cognitive load
|
|
14
|
+
- nesting
|
|
15
|
+
- line length
|
|
16
|
+
- naming
|
|
17
|
+
- guard clause
|
|
18
|
+
dependencies:
|
|
19
|
+
- quality/naming.md
|
|
20
|
+
- quality/functions.md
|
|
21
|
+
- quality/abstractions.md
|
|
22
|
+
- quality/comments.md
|
|
23
|
+
priority: primary
|
|
24
|
+
estimated_tokens: 1500
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Core Rules
|
|
28
|
+
|
|
29
|
+
### Cognitive Load
|
|
30
|
+
- Limit concepts per function
|
|
31
|
+
- Use meaningful names (see naming.md)
|
|
32
|
+
- Avoid clever tricks
|
|
33
|
+
- Prefer explicit over implicit
|
|
34
|
+
|
|
35
|
+
### Vertical Density
|
|
36
|
+
```python
|
|
37
|
+
# GOOD — spaced out
|
|
38
|
+
def process_user(user: User) -> Result:
|
|
39
|
+
validate(user)
|
|
40
|
+
|
|
41
|
+
enriched = enrich(user)
|
|
42
|
+
|
|
43
|
+
saved = save(enriched)
|
|
44
|
+
|
|
45
|
+
notify(saved)
|
|
46
|
+
|
|
47
|
+
return Result.ok(saved)
|
|
48
|
+
|
|
49
|
+
# BAD — cramped
|
|
50
|
+
def process_user(user): validate(user); enriched=enrich(user); saved=save(enriched); notify(saved); return Result.ok(saved)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Horizontal Density
|
|
54
|
+
```python
|
|
55
|
+
# GOOD — within line length
|
|
56
|
+
result = process(
|
|
57
|
+
user=user,
|
|
58
|
+
options=default_options,
|
|
59
|
+
callback=on_complete,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# BAD — too wide
|
|
63
|
+
result = process(user=user, options=default_options, callback=on_complete, timeout=30, retry=True, validate=True)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Line Length
|
|
67
|
+
- Target: 88-100 chars (configurable per project)
|
|
68
|
+
- Break long lines at logical points
|
|
69
|
+
- Use parentheses for implicit continuation
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
# Good breaks
|
|
73
|
+
long_function_call(
|
|
74
|
+
argument_one=value1,
|
|
75
|
+
argument_two=value2,
|
|
76
|
+
argument_three=value3,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Dictionary
|
|
80
|
+
config = {
|
|
81
|
+
"key_one": "value1",
|
|
82
|
+
"key_two": "value2",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Type hints
|
|
86
|
+
def func(
|
|
87
|
+
arg1: VeryLongTypeName,
|
|
88
|
+
arg2: AnotherVeryLongTypeName,
|
|
89
|
+
) -> ReturnTypeName:
|
|
90
|
+
...
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Blank Lines
|
|
94
|
+
- Between logical sections in function
|
|
95
|
+
- Between class methods
|
|
96
|
+
- Around top-level definitions (2 blank lines)
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
def process(data: Data) -> Result:
|
|
100
|
+
# Validation
|
|
101
|
+
if not data.is_valid:
|
|
102
|
+
return Result.error("invalid")
|
|
103
|
+
|
|
104
|
+
# Transformation
|
|
105
|
+
transformed = transform(data)
|
|
106
|
+
|
|
107
|
+
# Persistence
|
|
108
|
+
saved = save(transformed)
|
|
109
|
+
|
|
110
|
+
return Result.ok(saved)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Guard Clauses (Reduce Nesting)
|
|
114
|
+
```python
|
|
115
|
+
# GOOD — flat structure
|
|
116
|
+
def process(user: User) -> Result:
|
|
117
|
+
if not user.active:
|
|
118
|
+
return Result.error("inactive")
|
|
119
|
+
|
|
120
|
+
if not user.has_permission("write"):
|
|
121
|
+
return Result.error("forbidden")
|
|
122
|
+
|
|
123
|
+
# Main logic at base indent
|
|
124
|
+
data = fetch_data(user)
|
|
125
|
+
return Result.ok(transform(data))
|
|
126
|
+
|
|
127
|
+
# BAD — deeply nested
|
|
128
|
+
def process(user: User) -> Result:
|
|
129
|
+
if user.active:
|
|
130
|
+
if user.has_permission("write"):
|
|
131
|
+
data = fetch_data(user)
|
|
132
|
+
return Result.ok(transform(data))
|
|
133
|
+
else:
|
|
134
|
+
return Result.error("forbidden")
|
|
135
|
+
else:
|
|
136
|
+
return Result.error("inactive")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Named Intermediate Values
|
|
140
|
+
```python
|
|
141
|
+
# GOOD — self-documenting
|
|
142
|
+
def calculate_price(item: Item, user: User) -> Price:
|
|
143
|
+
base_price = item.base_price
|
|
144
|
+
discount = user.discount_rate
|
|
145
|
+
tax_rate = get_tax_rate(user.region)
|
|
146
|
+
|
|
147
|
+
discounted = base_price * (1 - discount)
|
|
148
|
+
final = discounted * (1 + tax_rate)
|
|
149
|
+
|
|
150
|
+
return Price(amount=final, currency=item.currency)
|
|
151
|
+
|
|
152
|
+
# BAD — magic calculations
|
|
153
|
+
def calculate_price(item: Item, user: User) -> Price:
|
|
154
|
+
return Price(amount=item.base_price * (1 - user.discount_rate) * (1 + get_tax_rate(user.region)), currency=item.currency)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Decision Rules
|
|
160
|
+
|
|
161
|
+
| Situation | Pattern |
|
|
162
|
+
|-----------|---------|
|
|
163
|
+
| Complex condition | Extract to variable/function |
|
|
164
|
+
| Long expression | Break across lines |
|
|
165
|
+
| Multiple operations | Separate with blank lines |
|
|
166
|
+
| Nested logic | Early return / guard clauses |
|
|
167
|
+
| Magic numbers | Named constants |
|
|
168
|
+
| Boolean flag controls behavior | Split into two functions |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Preferred Patterns
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
# Guard clauses (reduce nesting)
|
|
176
|
+
def process(user: User) -> Result:
|
|
177
|
+
if not user.active:
|
|
178
|
+
return Result.error("inactive")
|
|
179
|
+
|
|
180
|
+
if not user.has_permission("write"):
|
|
181
|
+
return Result.error("forbidden")
|
|
182
|
+
|
|
183
|
+
# Main logic at base indent
|
|
184
|
+
data = fetch_data(user)
|
|
185
|
+
return Result.ok(transform(data))
|
|
186
|
+
|
|
187
|
+
# Named intermediate values
|
|
188
|
+
def calculate_price(item: Item, user: User) -> Price:
|
|
189
|
+
base_price = item.base_price
|
|
190
|
+
discount = user.discount_rate
|
|
191
|
+
tax_rate = get_tax_rate(user.region)
|
|
192
|
+
|
|
193
|
+
discounted = base_price * (1 - discount)
|
|
194
|
+
final = discounted * (1 + tax_rate)
|
|
195
|
+
|
|
196
|
+
return Price(amount=final, currency=item.currency)
|
|
197
|
+
|
|
198
|
+
# Extract complex conditions
|
|
199
|
+
def is_eligible(user: User) -> bool:
|
|
200
|
+
has_active_sub = user.subscription and user.subscription.active
|
|
201
|
+
within_trial = user.trial_end and user.trial_end > datetime.now()
|
|
202
|
+
return has_active_sub or within_trial
|
|
203
|
+
|
|
204
|
+
def process(user: User) -> Result:
|
|
205
|
+
if not is_eligible(user):
|
|
206
|
+
return Result.error("not eligible")
|
|
207
|
+
...
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Avoid
|
|
213
|
+
|
|
214
|
+
- Single-letter variables (except loop counters: `i`, `j`, `k`)
|
|
215
|
+
- Abbreviations (`usr`, `cfg`, `msg` — use `user`, `config`, `message`)
|
|
216
|
+
- Deep nesting (>3 levels)
|
|
217
|
+
- Long functions (>50 lines)
|
|
218
|
+
- Multiple statements per line
|
|
219
|
+
- Clever one-liners that require mental parsing
|
|
220
|
+
- Single-letter type variables (`T`, `U`) without context
|
|
221
|
+
- Shadowing built-ins (`list`, `dict`, `str`, `type`, `id`)
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Validation Considerations
|
|
226
|
+
|
|
227
|
+
- Linter line-length checks (ruff: `line-length`)
|
|
228
|
+
- Cyclomatic complexity (radon, xenon)
|
|
229
|
+
- Code review readability assessment
|
|
230
|
+
- `ruff` rules: B007 (loop var in closure), B008 (func call in default)
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Related Skills
|
|
235
|
+
|
|
236
|
+
- `quality/naming.md`
|
|
237
|
+
- `quality/functions.md`
|
|
238
|
+
- `quality/abstractions.md`
|
|
239
|
+
- `quality/comments.md`
|