@booklib/skills 1.0.0 → 1.3.0
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.
- package/CONTRIBUTING.md +122 -0
- package/README.md +20 -1
- package/ROADMAP.md +36 -0
- package/animation-at-work/evals/evals.json +44 -0
- package/animation-at-work/examples/after.md +64 -0
- package/animation-at-work/examples/before.md +35 -0
- package/animation-at-work/scripts/audit_animations.py +295 -0
- package/bin/skills.js +552 -42
- package/clean-code-reviewer/SKILL.md +109 -1
- package/clean-code-reviewer/evals/evals.json +121 -3
- package/clean-code-reviewer/examples/after.md +48 -0
- package/clean-code-reviewer/examples/before.md +33 -0
- package/clean-code-reviewer/references/api_reference.md +158 -0
- package/clean-code-reviewer/references/practices-catalog.md +282 -0
- package/clean-code-reviewer/references/review-checklist.md +254 -0
- package/clean-code-reviewer/scripts/pre-review.py +206 -0
- package/data-intensive-patterns/evals/evals.json +43 -0
- package/data-intensive-patterns/examples/after.md +61 -0
- package/data-intensive-patterns/examples/before.md +38 -0
- package/data-intensive-patterns/scripts/adr.py +213 -0
- package/data-pipelines/evals/evals.json +45 -0
- package/data-pipelines/examples/after.md +97 -0
- package/data-pipelines/examples/before.md +37 -0
- package/data-pipelines/scripts/new_pipeline.py +444 -0
- package/design-patterns/evals/evals.json +46 -0
- package/design-patterns/examples/after.md +52 -0
- package/design-patterns/examples/before.md +29 -0
- package/design-patterns/scripts/scaffold.py +807 -0
- package/domain-driven-design/SKILL.md +120 -0
- package/domain-driven-design/evals/evals.json +48 -0
- package/domain-driven-design/examples/after.md +80 -0
- package/domain-driven-design/examples/before.md +43 -0
- package/domain-driven-design/scripts/scaffold.py +421 -0
- package/effective-java/evals/evals.json +46 -0
- package/effective-java/examples/after.md +83 -0
- package/effective-java/examples/before.md +37 -0
- package/effective-java/scripts/checkstyle_setup.py +211 -0
- package/effective-kotlin/evals/evals.json +45 -0
- package/effective-kotlin/examples/after.md +36 -0
- package/effective-kotlin/examples/before.md +38 -0
- package/effective-python/SKILL.md +199 -0
- package/effective-python/evals/evals.json +44 -0
- package/effective-python/examples/after.md +56 -0
- package/effective-python/examples/before.md +40 -0
- package/effective-python/ref-01-pythonic-thinking.md +202 -0
- package/effective-python/ref-02-lists-and-dicts.md +146 -0
- package/effective-python/ref-03-functions.md +186 -0
- package/effective-python/ref-04-comprehensions-generators.md +211 -0
- package/effective-python/ref-05-classes-interfaces.md +188 -0
- package/effective-python/ref-06-metaclasses-attributes.md +209 -0
- package/effective-python/ref-07-concurrency.md +213 -0
- package/effective-python/ref-08-robustness-performance.md +248 -0
- package/effective-python/ref-09-testing-debugging.md +253 -0
- package/effective-python/ref-10-collaboration.md +175 -0
- package/effective-python/references/api_reference.md +218 -0
- package/effective-python/references/practices-catalog.md +483 -0
- package/effective-python/references/review-checklist.md +190 -0
- package/effective-python/scripts/lint.py +173 -0
- package/kotlin-in-action/evals/evals.json +43 -0
- package/kotlin-in-action/examples/after.md +53 -0
- package/kotlin-in-action/examples/before.md +39 -0
- package/kotlin-in-action/scripts/setup_detekt.py +224 -0
- package/lean-startup/evals/evals.json +43 -0
- package/lean-startup/examples/after.md +80 -0
- package/lean-startup/examples/before.md +34 -0
- package/lean-startup/scripts/new_experiment.py +286 -0
- package/microservices-patterns/SKILL.md +140 -0
- package/microservices-patterns/evals/evals.json +45 -0
- package/microservices-patterns/examples/after.md +69 -0
- package/microservices-patterns/examples/before.md +40 -0
- package/microservices-patterns/scripts/new_service.py +583 -0
- package/package.json +1 -1
- package/refactoring-ui/evals/evals.json +45 -0
- package/refactoring-ui/examples/after.md +85 -0
- package/refactoring-ui/examples/before.md +58 -0
- package/refactoring-ui/scripts/audit_css.py +250 -0
- package/skill-router/SKILL.md +142 -0
- package/skill-router/evals/evals.json +38 -0
- package/skill-router/examples/after.md +63 -0
- package/skill-router/examples/before.md +39 -0
- package/skill-router/references/api_reference.md +24 -0
- package/skill-router/references/routing-heuristics.md +89 -0
- package/skill-router/references/skill-catalog.md +156 -0
- package/skill-router/scripts/route.py +266 -0
- package/storytelling-with-data/evals/evals.json +47 -0
- package/storytelling-with-data/examples/after.md +50 -0
- package/storytelling-with-data/examples/before.md +33 -0
- package/storytelling-with-data/scripts/chart_review.py +301 -0
- package/system-design-interview/evals/evals.json +45 -0
- package/system-design-interview/examples/after.md +94 -0
- package/system-design-interview/examples/before.md +27 -0
- package/system-design-interview/scripts/new_design.py +421 -0
- package/using-asyncio-python/evals/evals.json +43 -0
- package/using-asyncio-python/examples/after.md +68 -0
- package/using-asyncio-python/examples/before.md +39 -0
- package/using-asyncio-python/scripts/check_blocking.py +270 -0
- package/web-scraping-python/evals/evals.json +46 -0
- package/web-scraping-python/examples/after.md +109 -0
- package/web-scraping-python/examples/before.md +40 -0
- package/web-scraping-python/scripts/new_scraper.py +231 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
checkstyle_setup.py - Set up Checkstyle with an Effective Java-aligned configuration.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python checkstyle_setup.py [--output-dir ./]
|
|
7
|
+
|
|
8
|
+
Generates:
|
|
9
|
+
effective-java-checkstyle.xml - Checkstyle rules mapped to Effective Java items
|
|
10
|
+
run_checkstyle.sh - Shell script to run Checkstyle on src/
|
|
11
|
+
|
|
12
|
+
Each config section references the Effective Java item it enforces.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import os
|
|
17
|
+
import pathlib
|
|
18
|
+
import stat
|
|
19
|
+
import sys
|
|
20
|
+
import urllib.request
|
|
21
|
+
import urllib.error
|
|
22
|
+
|
|
23
|
+
CHECKSTYLE_VERSION = "10.14.2"
|
|
24
|
+
CHECKSTYLE_JAR = f"checkstyle-{CHECKSTYLE_VERSION}-all.jar"
|
|
25
|
+
CHECKSTYLE_URL = (
|
|
26
|
+
f"https://github.com/checkstyle/checkstyle/releases/download/"
|
|
27
|
+
f"checkstyle-{CHECKSTYLE_VERSION}/{CHECKSTYLE_JAR}"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
CHECKSTYLE_XML = """\
|
|
31
|
+
<?xml version="1.0"?>
|
|
32
|
+
<!DOCTYPE module PUBLIC
|
|
33
|
+
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
|
34
|
+
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
|
35
|
+
|
|
36
|
+
<!--
|
|
37
|
+
Effective Java - Checkstyle Configuration
|
|
38
|
+
Each module references the Effective Java item it enforces.
|
|
39
|
+
Book: "Effective Java" by Joshua Bloch, 3rd Edition.
|
|
40
|
+
-->
|
|
41
|
+
<module name="Checker">
|
|
42
|
+
<property name="severity" value="warning"/>
|
|
43
|
+
|
|
44
|
+
<module name="TreeWalker">
|
|
45
|
+
|
|
46
|
+
<!--
|
|
47
|
+
Item 15: Minimize the accessibility of classes and members.
|
|
48
|
+
Prefer private fields; expose only what is necessary.
|
|
49
|
+
-->
|
|
50
|
+
<module name="VisibilityModifier">
|
|
51
|
+
<property name="protectedAllowed" value="false"/>
|
|
52
|
+
<property name="publicMemberPattern" value="^serialVersionUID$"/>
|
|
53
|
+
</module>
|
|
54
|
+
|
|
55
|
+
<!--
|
|
56
|
+
Item 17: Minimize mutability — utility/helper classes should be final.
|
|
57
|
+
FinalClass flags classes with only private constructors that are not final.
|
|
58
|
+
-->
|
|
59
|
+
<module name="FinalClass"/>
|
|
60
|
+
|
|
61
|
+
<!--
|
|
62
|
+
Item 10: Obey the general contract when overriding equals.
|
|
63
|
+
Always override hashCode when you override equals.
|
|
64
|
+
-->
|
|
65
|
+
<module name="EqualsHashCode"/>
|
|
66
|
+
|
|
67
|
+
<!--
|
|
68
|
+
Item 34: Use enums instead of int constants.
|
|
69
|
+
Magic numbers in code signal that an enum or named constant should be used.
|
|
70
|
+
-->
|
|
71
|
+
<module name="MagicNumber">
|
|
72
|
+
<property name="ignoreNumbers" value="-1, 0, 1, 2"/>
|
|
73
|
+
<property name="ignoreAnnotation" value="true"/>
|
|
74
|
+
<property name="ignoreHashCodeMethod" value="true"/>
|
|
75
|
+
</module>
|
|
76
|
+
|
|
77
|
+
<!--
|
|
78
|
+
Item 77: Don't ignore exceptions.
|
|
79
|
+
Empty catch blocks hide failures; always handle or rethrow.
|
|
80
|
+
-->
|
|
81
|
+
<module name="EmptyCatchBlock">
|
|
82
|
+
<property name="exceptionVariableName" value="expected|ignore"/>
|
|
83
|
+
</module>
|
|
84
|
+
|
|
85
|
+
<!--
|
|
86
|
+
Item 2: Consider a builder when faced with many constructor parameters.
|
|
87
|
+
More than 3 parameters is a signal to introduce a Builder.
|
|
88
|
+
-->
|
|
89
|
+
<module name="ParameterNumber">
|
|
90
|
+
<property name="max" value="3"/>
|
|
91
|
+
<property name="tokens" value="METHOD_DEF, CTOR_DEF"/>
|
|
92
|
+
</module>
|
|
93
|
+
|
|
94
|
+
<!--
|
|
95
|
+
General best practice aligned with Effective Java's emphasis on small,
|
|
96
|
+
focused methods. Methods longer than 30 lines are harder to reason about.
|
|
97
|
+
-->
|
|
98
|
+
<module name="MethodLength">
|
|
99
|
+
<property name="max" value="30"/>
|
|
100
|
+
<property name="countEmpty" value="false"/>
|
|
101
|
+
</module>
|
|
102
|
+
|
|
103
|
+
<!--
|
|
104
|
+
Item 11: Always override toString.
|
|
105
|
+
Not directly checkable, but HideUtilityClassConstructor complements
|
|
106
|
+
Item 4 (enforce non-instantiability with private constructor).
|
|
107
|
+
-->
|
|
108
|
+
<module name="HideUtilityClassConstructor"/>
|
|
109
|
+
|
|
110
|
+
<!--
|
|
111
|
+
Item 57: Minimize the scope of local variables.
|
|
112
|
+
Inner assignments make scope harder to reason about.
|
|
113
|
+
-->
|
|
114
|
+
<module name="InnerAssignment"/>
|
|
115
|
+
|
|
116
|
+
<!--
|
|
117
|
+
Item 67: Optimize judiciously — avoid redundant string concatenation in loops.
|
|
118
|
+
-->
|
|
119
|
+
<module name="StringLiteralEquality"/>
|
|
120
|
+
|
|
121
|
+
</module>
|
|
122
|
+
</module>
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
RUN_SCRIPT = """\
|
|
126
|
+
#!/usr/bin/env bash
|
|
127
|
+
# run_checkstyle.sh
|
|
128
|
+
# Runs Checkstyle with the Effective Java configuration against src/
|
|
129
|
+
# Generated by checkstyle_setup.py
|
|
130
|
+
|
|
131
|
+
set -euo pipefail
|
|
132
|
+
|
|
133
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
134
|
+
JAR="${SCRIPT_DIR}/{jar}"
|
|
135
|
+
CONFIG="${SCRIPT_DIR}/effective-java-checkstyle.xml"
|
|
136
|
+
SRC_DIR="${1:-src}"
|
|
137
|
+
|
|
138
|
+
if [[ ! -f "$JAR" ]]; then
|
|
139
|
+
echo "ERROR: Checkstyle jar not found at $JAR"
|
|
140
|
+
echo "Run: python checkstyle_setup.py --output-dir \\"$(dirname "$JAR")\\""
|
|
141
|
+
exit 1
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
if [[ ! -d "$SRC_DIR" ]]; then
|
|
145
|
+
echo "ERROR: Source directory not found: $SRC_DIR"
|
|
146
|
+
echo "Usage: ./run_checkstyle.sh [src-dir]"
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
echo "Running Checkstyle on $SRC_DIR ..."
|
|
151
|
+
java -jar "$JAR" -c "$CONFIG" -r "$SRC_DIR"
|
|
152
|
+
echo "Done."
|
|
153
|
+
""".format(jar=CHECKSTYLE_JAR)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def download_jar(output_dir: pathlib.Path) -> bool:
|
|
157
|
+
jar_path = output_dir / CHECKSTYLE_JAR
|
|
158
|
+
if jar_path.exists():
|
|
159
|
+
print(f"Checkstyle jar already present: {jar_path}")
|
|
160
|
+
return True
|
|
161
|
+
print(f"Downloading Checkstyle {CHECKSTYLE_VERSION} from GitHub ...")
|
|
162
|
+
try:
|
|
163
|
+
urllib.request.urlretrieve(CHECKSTYLE_URL, jar_path)
|
|
164
|
+
print(f"Downloaded: {jar_path}")
|
|
165
|
+
return True
|
|
166
|
+
except urllib.error.URLError as exc:
|
|
167
|
+
print(f"Download failed: {exc}")
|
|
168
|
+
print()
|
|
169
|
+
print("To install manually:")
|
|
170
|
+
print(f" curl -L -o {jar_path} \\")
|
|
171
|
+
print(f" {CHECKSTYLE_URL}")
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def write_file(path: pathlib.Path, content: str, executable: bool = False) -> None:
|
|
176
|
+
path.write_text(content, encoding="utf-8")
|
|
177
|
+
if executable:
|
|
178
|
+
path.chmod(path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
179
|
+
print(f"Wrote: {path}")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def main() -> None:
|
|
183
|
+
parser = argparse.ArgumentParser(
|
|
184
|
+
description="Set up Checkstyle with an Effective Java-aligned configuration."
|
|
185
|
+
)
|
|
186
|
+
parser.add_argument(
|
|
187
|
+
"--output-dir",
|
|
188
|
+
default=".",
|
|
189
|
+
help="Directory to write config files and download the jar (default: ./)",
|
|
190
|
+
)
|
|
191
|
+
args = parser.parse_args()
|
|
192
|
+
|
|
193
|
+
output_dir = pathlib.Path(args.output_dir).resolve()
|
|
194
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
195
|
+
|
|
196
|
+
download_jar(output_dir)
|
|
197
|
+
|
|
198
|
+
write_file(output_dir / "effective-java-checkstyle.xml", CHECKSTYLE_XML)
|
|
199
|
+
write_file(output_dir / "run_checkstyle.sh", RUN_SCRIPT, executable=True)
|
|
200
|
+
|
|
201
|
+
print()
|
|
202
|
+
print("Setup complete.")
|
|
203
|
+
print(f" Config : {output_dir / 'effective-java-checkstyle.xml'}")
|
|
204
|
+
print(f" Runner : {output_dir / 'run_checkstyle.sh'}")
|
|
205
|
+
print()
|
|
206
|
+
print("Run Checkstyle:")
|
|
207
|
+
print(f" cd {output_dir} && ./run_checkstyle.sh [src-dir]")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
if __name__ == "__main__":
|
|
211
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"evals": [
|
|
3
|
+
{
|
|
4
|
+
"id": "eval-01-mutable-var-nullable-overuse",
|
|
5
|
+
"prompt": "Review this Kotlin code:\n\n```kotlin\nclass UserSession {\n var userId: String? = null\n var userName: String? = null\n var email: String? = null\n var isLoggedIn: Boolean = false\n var loginTime: Long? = null\n var sessionToken: String? = null\n var permissionLevel: Int = 0\n \n fun login(userId: String, userName: String, email: String, token: String, level: Int) {\n this.userId = userId\n this.userName = userName\n this.email = email\n this.isLoggedIn = true\n this.loginTime = System.currentTimeMillis()\n this.sessionToken = token\n this.permissionLevel = level\n }\n \n fun logout() {\n this.userId = null\n this.userName = null\n this.email = null\n this.isLoggedIn = false\n this.loginTime = null\n this.sessionToken = null\n this.permissionLevel = 0\n }\n \n fun getDisplayName(): String {\n if (userName != null) {\n return userName!!\n }\n return \"Guest\"\n }\n \n fun isAdmin(): Boolean {\n if (permissionLevel != null) {\n return permissionLevel!! >= 10\n }\n return false\n }\n}\n```",
|
|
6
|
+
"expectations": [
|
|
7
|
+
"Flags Item 1 (Limit mutability): all properties are var but most could be val if the design is reconsidered — the entire class should likely be replaced with two states (LoggedIn / LoggedOut) using a sealed class",
|
|
8
|
+
"Flags Item 8 (Avoid !! operator): getDisplayName() uses userName!! after a null check — should use userName ?: \"Guest\" with Elvis or smart cast",
|
|
9
|
+
"Notes that permissionLevel is Int (non-nullable) but isAdmin() checks 'if (permissionLevel != null)' — this check is always true and indicates confusion about nullability",
|
|
10
|
+
"Flags that nullable types (String?, Long?) are used for all properties even though they are only null in the logged-out state — this is a design smell, not a nullability need",
|
|
11
|
+
"Suggests modeling this with a sealed class: sealed class SessionState with data class LoggedIn(...) and object LoggedOut — making illegal states unrepresentable",
|
|
12
|
+
"References Item 2 (Minimize variable scope): all these properties have class-wide scope when they only have meaning in the logged-in state",
|
|
13
|
+
"Notes that the login() function taking 5 positional parameters (Item 17: named arguments) is error-prone — a data class or named arguments should be used"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "eval-02-missing-data-class-extension",
|
|
18
|
+
"prompt": "Review this Kotlin code:\n\n```kotlin\nclass Point {\n var x: Double\n var y: Double\n \n constructor(x: Double, y: Double) {\n this.x = x\n this.y = y\n }\n \n fun distanceTo(other: Point): Double {\n val dx = this.x - other.x\n val dy = this.y - other.y\n return Math.sqrt(dx * dx + dy * dy)\n }\n \n override fun toString(): String {\n return \"Point(\" + x + \", \" + y + \")\"\n }\n}\n\nfun translatePoints(points: List<Point>, dx: Double, dy: Double): List<Point> {\n val result = mutableListOf<Point>()\n for (point in points) {\n val newPoint = Point(point.x + dx, point.y + dy)\n result.add(newPoint)\n }\n return result\n}\n\nfun findClosestTo(points: List<Point>, target: Point): Point? {\n var closest: Point? = null\n var minDist = Double.MAX_VALUE\n for (point in points) {\n val dist = point.distanceTo(target)\n if (dist < minDist) {\n minDist = dist\n closest = point\n }\n }\n return closest\n}\n```",
|
|
19
|
+
"expectations": [
|
|
20
|
+
"Flags that Point should be a data class — it has structural equality semantics but lacks the data modifier, meaning equals() and hashCode() use reference equality by default",
|
|
21
|
+
"Flags Item 1 (Limit mutability): Point fields are var but a geometric point is naturally immutable — they should be val",
|
|
22
|
+
"Notes that translatePoints uses an imperative for-loop with a mutable list where map { Point(it.x + dx, it.y + dy) } is idiomatic and more readable",
|
|
23
|
+
"Notes that findClosestTo uses an imperative loop where minByOrNull { it.distanceTo(target) } from the stdlib is idiomatic (Item 20: use stdlib algorithms)",
|
|
24
|
+
"Points out that Math.sqrt() is Java interop — Kotlin has kotlin.math.sqrt() which is more idiomatic",
|
|
25
|
+
"May suggest an extension function Point.translate(dx: Double, dy: Double) as a cleaner API that could also be placed in the data class with copy()",
|
|
26
|
+
"Notes that the string concatenation in toString() is replaced automatically by data class, but if kept manually, string templates are idiomatic: \"Point($x, $y)\""
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "eval-03-idiomatic-kotlin-already-good",
|
|
31
|
+
"prompt": "Review this Kotlin code:\n\n```kotlin\nsealed interface PaymentResult {\n data class Success(val transactionId: String, val amount: Money) : PaymentResult\n data class Failure(val code: ErrorCode, val message: String) : PaymentResult\n data object Pending : PaymentResult\n}\n\nenum class ErrorCode { INSUFFICIENT_FUNDS, CARD_DECLINED, NETWORK_ERROR, FRAUD_DETECTED }\n\n@JvmInline\nvalue class Money(val cents: Long) {\n init {\n require(cents >= 0) { \"Money cannot be negative: $cents cents\" }\n }\n \n operator fun plus(other: Money) = Money(cents + other.cents)\n operator fun minus(other: Money): Money {\n require(other.cents <= cents) { \"Cannot subtract more than available\" }\n return Money(cents - other.cents)\n }\n \n override fun toString() = \"$${cents / 100}.${(cents % 100).toString().padStart(2, '0')}\"\n}\n\nfun interface PaymentGateway {\n suspend fun charge(amount: Money, token: String): PaymentResult\n}\n\nsuspend fun processWithRetry(\n gateway: PaymentGateway,\n amount: Money,\n token: String,\n maxAttempts: Int = 3\n): PaymentResult {\n repeat(maxAttempts) { attempt ->\n val result = gateway.charge(amount, token)\n when (result) {\n is PaymentResult.Success -> return result\n is PaymentResult.Failure -> {\n if (result.code != ErrorCode.NETWORK_ERROR || attempt == maxAttempts - 1) return result\n }\n PaymentResult.Pending -> return result\n }\n }\n return PaymentResult.Failure(ErrorCode.NETWORK_ERROR, \"Max retries exceeded\")\n}\n```",
|
|
32
|
+
"expectations": [
|
|
33
|
+
"Recognizes this is already idiomatic, well-designed Kotlin and says so explicitly",
|
|
34
|
+
"Praises the use of sealed interface for PaymentResult — exhaustive when expressions, extensible outside the module",
|
|
35
|
+
"Praises @JvmInline value class Money — Item 46/49 efficiency (no boxing overhead), type-safe, with require() precondition (Item 5)",
|
|
36
|
+
"Praises operator overloading on Money — follows convention (plus/minus) and is semantically meaningful (Item 12)",
|
|
37
|
+
"Praises fun interface PaymentGateway — SAM interface enabling lambda usage, clean abstraction",
|
|
38
|
+
"Praises using repeat with when instead of a for-loop with if/else — idiomatic control flow",
|
|
39
|
+
"Praises the sealed hierarchy discriminating between Pending and Failure so the retry logic doesn't retry non-network errors",
|
|
40
|
+
"Does NOT manufacture fake issues just to have something to say",
|
|
41
|
+
"May suggest minor optional improvements (e.g., a Currency field on Money for multi-currency support) but clearly frames them as out-of-scope for the given context"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# After
|
|
2
|
+
|
|
3
|
+
Idiomatic Kotlin with `val` properties, non-null types where absence is not meaningful, and a functional pipeline replacing the manual while loop.
|
|
4
|
+
|
|
5
|
+
```kotlin
|
|
6
|
+
class UserReportGenerator(
|
|
7
|
+
private val users: List<User>,
|
|
8
|
+
private val reportTitle: String,
|
|
9
|
+
private val includeInactive: Boolean = false,
|
|
10
|
+
) {
|
|
11
|
+
|
|
12
|
+
fun generateSummary(): String {
|
|
13
|
+
val activeUsers = if (includeInactive) users else users.filter { it.active }
|
|
14
|
+
|
|
15
|
+
val lines = activeUsers.map { user ->
|
|
16
|
+
buildString {
|
|
17
|
+
append("${user.name} (${user.email})")
|
|
18
|
+
user.role?.let { append(" - $it") }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return buildString {
|
|
23
|
+
appendLine(reportTitle)
|
|
24
|
+
lines.forEach(::appendLine)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Key improvements:
|
|
31
|
+
- Constructor parameters replace mutable properties (Item 1: Limit Mutability) — the generator is now immutable after construction
|
|
32
|
+
- `users: List<User>` and `reportTitle: String` are non-null types; nullability would only be meaningful if absence were valid (Item 8: Handle Nulls Properly)
|
|
33
|
+
- `filter`, `map`, and `buildString` replace the manual `while` index loop — idiomatic stdlib use (Item 20: Use stdlib algorithms)
|
|
34
|
+
- `user.role?.let { append(" - $it") }` replaces the null-check `if` block with a safe-call chain (Item 8)
|
|
35
|
+
- `includeInactive = false` as a default parameter eliminates the need for an overloaded constructor (Item 34: Consider named and optional args)
|
|
36
|
+
- `generateSummary()` returns `String` (non-null) — the empty report is a valid empty string, not `null` (Item 7: Prefer null or Failure over exceptions for expected failures)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Before
|
|
2
|
+
|
|
3
|
+
Kotlin code written in Java style with `var` everywhere, misused nullability, and index-based loops that ignore idiomatic Kotlin features.
|
|
4
|
+
|
|
5
|
+
```kotlin
|
|
6
|
+
class UserReportGenerator {
|
|
7
|
+
|
|
8
|
+
var users: MutableList<User>? = null
|
|
9
|
+
var reportTitle: String? = null
|
|
10
|
+
var includeInactive: Boolean = false
|
|
11
|
+
|
|
12
|
+
fun generateSummary(): String? {
|
|
13
|
+
var result = ""
|
|
14
|
+
if (users == null) {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
result = result + reportTitle + "\n"
|
|
18
|
+
var i = 0
|
|
19
|
+
while (i < users!!.size) {
|
|
20
|
+
val user = users!![i]
|
|
21
|
+
if (includeInactive == false) {
|
|
22
|
+
if (user.active == false) {
|
|
23
|
+
i++
|
|
24
|
+
continue
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
var line = ""
|
|
28
|
+
line = line + user.name + " (" + user.email + ")"
|
|
29
|
+
if (user.role != null) {
|
|
30
|
+
line = line + " - " + user.role
|
|
31
|
+
}
|
|
32
|
+
result = result + line + "\n"
|
|
33
|
+
i++
|
|
34
|
+
}
|
|
35
|
+
return result
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: effective-python
|
|
3
|
+
description: Review existing Python code and write new Python code following the 90 best practices from "Effective Python" by Brett Slatkin (2nd Edition). Use when writing Python, reviewing Python code, or wanting idiomatic, Pythonic solutions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Effective Python Skill
|
|
7
|
+
|
|
8
|
+
Apply the 90 items from Brett Slatkin's "Effective Python" (2nd Edition) to review existing code and write new Python code. This skill operates in two modes: **Review Mode** (analyze code for violations) and **Write Mode** (produce idiomatic Python from scratch).
|
|
9
|
+
|
|
10
|
+
## Reference Files
|
|
11
|
+
|
|
12
|
+
This skill includes categorized reference files with all 90 items:
|
|
13
|
+
|
|
14
|
+
- `ref-01-pythonic-thinking.md` — Items 1-10: PEP 8, f-strings, bytes/str, walrus operator, unpacking, enumerate, zip, slicing
|
|
15
|
+
- `ref-02-lists-and-dicts.md` — Items 11-18: Slicing, sorting, dict ordering, defaultdict, __missing__
|
|
16
|
+
- `ref-03-functions.md` — Items 19-26: Exceptions vs None, closures, *args/**kwargs, keyword-only args, decorators
|
|
17
|
+
- `ref-04-comprehensions-generators.md` — Items 27-36: Comprehensions, generators, yield from, itertools
|
|
18
|
+
- `ref-05-classes-interfaces.md` — Items 37-43: Composition, @classmethod, super(), mix-ins, public attrs
|
|
19
|
+
- `ref-06-metaclasses-attributes.md` — Items 44-51: @property, descriptors, __getattr__, __init_subclass__, class decorators
|
|
20
|
+
- `ref-07-concurrency.md` — Items 52-64: subprocess, threads, Lock, Queue, coroutines, asyncio
|
|
21
|
+
- `ref-08-robustness-performance.md` — Items 65-76: try/except, contextlib, datetime, decimal, profiling, data structures
|
|
22
|
+
- `ref-09-testing-debugging.md` — Items 77-85: TestCase, mocks, dependency injection, pdb, tracemalloc
|
|
23
|
+
- `ref-10-collaboration.md` — Items 86-90: Docstrings, packages, root exceptions, virtual environments
|
|
24
|
+
|
|
25
|
+
## How to Use This Skill
|
|
26
|
+
|
|
27
|
+
**Before responding**, read the relevant reference files based on the code's topic. For a general review, read all files. For targeted work (e.g., writing async code), read the specific reference (e.g., `ref-07-concurrency.md`).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Mode 1: Code Review
|
|
32
|
+
|
|
33
|
+
When the user asks you to **review** existing Python code, follow this process:
|
|
34
|
+
|
|
35
|
+
### Step 1: Read Relevant References
|
|
36
|
+
Determine which chapters apply to the code under review and read those reference files. If unsure, read all of them.
|
|
37
|
+
|
|
38
|
+
### Step 2: Analyze the Code
|
|
39
|
+
For each relevant item from the book, check whether the code follows or violates the guideline. Focus on:
|
|
40
|
+
|
|
41
|
+
1. **Style and Idiom** (Items 1-10): Is it Pythonic? Does it use f-strings, unpacking, enumerate, zip properly?
|
|
42
|
+
2. **Data Structures** (Items 11-18): Are lists and dicts used correctly? Is sorting done with key functions?
|
|
43
|
+
3. **Function Design** (Items 19-26): Do functions raise exceptions instead of returning None? Are args well-structured?
|
|
44
|
+
4. **Comprehensions & Generators** (Items 27-36): Are comprehensions preferred over map/filter? Are generators used for large sequences?
|
|
45
|
+
5. **Class Design** (Items 37-43): Is composition preferred over deep nesting? Are mix-ins used correctly?
|
|
46
|
+
6. **Metaclasses & Attributes** (Items 44-51): Are plain attributes used instead of getter/setter methods? Is @property used appropriately?
|
|
47
|
+
7. **Concurrency** (Items 52-64): Are threads used only for I/O? Is asyncio structured correctly?
|
|
48
|
+
8. **Robustness** (Items 65-76): Is error handling structured with try/except/else/finally? Are the right data structures chosen?
|
|
49
|
+
9. **Testing** (Items 77-85): Are tests well-structured? Are mocks used appropriately?
|
|
50
|
+
10. **Collaboration** (Items 86-90): Are docstrings present? Are APIs stable?
|
|
51
|
+
|
|
52
|
+
### Step 3: Report Findings
|
|
53
|
+
For each issue found, report:
|
|
54
|
+
- **Item number and name** (e.g., "Item 4: Prefer Interpolated F-Strings")
|
|
55
|
+
- **Location** in the code
|
|
56
|
+
- **What's wrong** (the anti-pattern)
|
|
57
|
+
- **How to fix it** (the Pythonic way)
|
|
58
|
+
- **Priority**: Critical (bugs/correctness), Important (maintainability), Suggestion (style)
|
|
59
|
+
|
|
60
|
+
### Step 4: Provide Fixed Code
|
|
61
|
+
Offer a corrected version of the code with all issues addressed, with comments explaining each change.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Mode 2: Writing New Code
|
|
66
|
+
|
|
67
|
+
When the user asks you to **write** new Python code, follow these principles:
|
|
68
|
+
|
|
69
|
+
### Always Apply These Core Practices
|
|
70
|
+
|
|
71
|
+
1. **Follow PEP 8** — Use consistent naming (snake_case for functions/variables, PascalCase for classes). Use `pylint` and `black`-compatible style.
|
|
72
|
+
|
|
73
|
+
2. **Use f-strings** for string formatting (Item 4). Never use % or .format() for simple cases.
|
|
74
|
+
|
|
75
|
+
3. **Use unpacking** instead of indexing (Item 6). Prefer `first, second = my_list` over `my_list[0]`.
|
|
76
|
+
|
|
77
|
+
4. **Use enumerate** instead of range(len(...)) (Item 7).
|
|
78
|
+
|
|
79
|
+
5. **Use zip** to iterate over multiple lists in parallel (Item 8). Use `zip_longest` from itertools when lengths differ.
|
|
80
|
+
|
|
81
|
+
6. **Avoid else blocks** after for/while loops (Item 9).
|
|
82
|
+
|
|
83
|
+
7. **Use assignment expressions** (:= walrus operator) to reduce repetition when appropriate (Item 10).
|
|
84
|
+
|
|
85
|
+
8. **Raise exceptions** instead of returning None for failure cases (Item 20).
|
|
86
|
+
|
|
87
|
+
9. **Use keyword-only arguments** for clarity (Item 25). Use positional-only args to separate API from implementation (Item 25).
|
|
88
|
+
|
|
89
|
+
10. **Use functools.wraps** on all decorators (Item 26).
|
|
90
|
+
|
|
91
|
+
11. **Prefer comprehensions** over map/filter (Item 27). Keep them simple — no more than two expressions (Item 28).
|
|
92
|
+
|
|
93
|
+
12. **Use generators** for large sequences instead of returning lists (Item 30).
|
|
94
|
+
|
|
95
|
+
13. **Prefer composition** over deeply nested classes (Item 37).
|
|
96
|
+
|
|
97
|
+
14. **Use @classmethod** for polymorphic constructors (Item 39).
|
|
98
|
+
|
|
99
|
+
15. **Always call super().__init__** (Item 40).
|
|
100
|
+
|
|
101
|
+
16. **Use plain attributes** instead of getter/setter methods. Use @property for special behavior (Item 44).
|
|
102
|
+
|
|
103
|
+
17. **Use try/except/else/finally** structure correctly (Item 65).
|
|
104
|
+
|
|
105
|
+
18. **Write docstrings** for every module, class, and function (Item 84).
|
|
106
|
+
|
|
107
|
+
### Code Structure Template
|
|
108
|
+
|
|
109
|
+
When writing new modules or classes, follow this structure:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
"""Module docstring describing purpose."""
|
|
113
|
+
|
|
114
|
+
# Standard library imports
|
|
115
|
+
# Third-party imports
|
|
116
|
+
# Local imports
|
|
117
|
+
|
|
118
|
+
# Module-level constants
|
|
119
|
+
|
|
120
|
+
class MyClass:
|
|
121
|
+
"""Class docstring describing purpose and usage.
|
|
122
|
+
|
|
123
|
+
Attributes:
|
|
124
|
+
attr_name: Description of attribute.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def __init__(self, param: type) -> None:
|
|
128
|
+
"""Initialize with description of params."""
|
|
129
|
+
self.param = param # Use public attributes (Item 42)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def from_alternative(cls, data):
|
|
133
|
+
"""Alternative constructor (Item 39)."""
|
|
134
|
+
return cls(processed_data)
|
|
135
|
+
|
|
136
|
+
def method(self, arg: type) -> return_type:
|
|
137
|
+
"""Method docstring.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
arg: Description.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Description of return value.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
ValueError: When arg is invalid (Item 20).
|
|
147
|
+
"""
|
|
148
|
+
pass
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Concurrency Guidelines
|
|
152
|
+
|
|
153
|
+
- Use `subprocess` for managing child processes (Item 52)
|
|
154
|
+
- Use threads **only** for blocking I/O, never for parallelism (Item 53)
|
|
155
|
+
- Use `threading.Lock` to prevent data races (Item 54)
|
|
156
|
+
- Use `Queue` for coordinating work between threads (Item 55)
|
|
157
|
+
- Use `asyncio` for highly concurrent I/O (Item 60)
|
|
158
|
+
- Never mix blocking calls in async code (Item 62)
|
|
159
|
+
|
|
160
|
+
### Testing Guidelines
|
|
161
|
+
|
|
162
|
+
- Subclass `TestCase` and use `setUp`/`tearDown` (Item 78)
|
|
163
|
+
- Use `unittest.mock` for complex dependencies (Item 78)
|
|
164
|
+
- Encapsulate dependencies to make code testable (Item 79)
|
|
165
|
+
- Use `pdb.set_trace()` or `breakpoint()` for debugging (Item 80)
|
|
166
|
+
- Use `tracemalloc` for memory debugging (Item 81)
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Priority of Items by Impact
|
|
171
|
+
|
|
172
|
+
When time is limited, focus on these highest-impact items first:
|
|
173
|
+
|
|
174
|
+
### Critical (Correctness & Bugs)
|
|
175
|
+
- Item 20: Raise exceptions instead of returning None
|
|
176
|
+
- Item 53: Use threads for I/O only, not parallelism
|
|
177
|
+
- Item 54: Use Lock to prevent data races
|
|
178
|
+
- Item 40: Initialize parent classes with super()
|
|
179
|
+
- Item 65: Use try/except/else/finally correctly
|
|
180
|
+
- Item 73: Use datetime instead of time module for timezone handling
|
|
181
|
+
|
|
182
|
+
### Important (Maintainability)
|
|
183
|
+
- Item 1: Follow PEP 8 style
|
|
184
|
+
- Item 4: Use f-strings
|
|
185
|
+
- Item 19: Never unpack more than 3 variables
|
|
186
|
+
- Item 25: Use keyword-only and positional-only arguments
|
|
187
|
+
- Item 26: Use functools.wraps for decorators
|
|
188
|
+
- Item 37: Compose classes instead of deep nesting
|
|
189
|
+
- Item 42: Prefer public attributes over private
|
|
190
|
+
- Item 44: Use plain attributes over getter/setter
|
|
191
|
+
- Item 84: Write docstrings for all public APIs
|
|
192
|
+
|
|
193
|
+
### Suggestions (Polish & Optimization)
|
|
194
|
+
- Item 7: Use enumerate instead of range
|
|
195
|
+
- Item 8: Use zip for parallel iteration
|
|
196
|
+
- Item 10: Use walrus operator to reduce repetition
|
|
197
|
+
- Item 27: Use comprehensions over map/filter
|
|
198
|
+
- Item 30: Use generators for large sequences
|
|
199
|
+
- Item 70: Profile before optimizing (cProfile)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"evals": [
|
|
3
|
+
{
|
|
4
|
+
"id": "eval-01-loop-except-mutable-default",
|
|
5
|
+
"prompt": "Review this Python code:\n\n```python\ndef process_orders(orders, results=[]):\n for i in range(len(orders)):\n order = orders[i]\n try:\n total = order['price'] * order['quantity']\n results.append({'id': order['id'], 'total': total})\n except:\n pass\n\n processed = []\n for item in results:\n processed.append(item['total'])\n return processed\n\n\ndef get_high_value(orders, threshold):\n high = []\n for order in orders:\n if order['total'] > threshold:\n high.append(order)\n return high\n```",
|
|
6
|
+
"expectations": [
|
|
7
|
+
"Flags the mutable default argument `results=[]` as a critical bug (Item 24: Use None and docstrings for dynamic default arguments)",
|
|
8
|
+
"Flags bare `except:` that silently swallows all exceptions including KeyboardInterrupt and SystemExit (Item 65: always catch specific exception types)",
|
|
9
|
+
"Identifies `for i in range(len(orders))` as non-idiomatic; recommends `for order in orders` with enumerate if index is needed (Item 7)",
|
|
10
|
+
"Identifies the manual list-building loop in `process_orders` that should be a list comprehension (Item 27)",
|
|
11
|
+
"Identifies the manual list-building loop in `get_high_value` that should be a list comprehension (Item 27)",
|
|
12
|
+
"Provides a corrected version that uses `None` as default and initializes inside the function",
|
|
13
|
+
"Provides corrected versions using list comprehensions for both loops"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "eval-02-class-missing-dataclass",
|
|
18
|
+
"prompt": "Review this Python code:\n\n```python\nclass Product:\n def __init__(self, name, price, category, in_stock=True):\n self.name = name\n self.price = price\n self.category = category\n self.in_stock = in_stock\n\n def get_name(self):\n return self.name\n\n def set_price(self, price):\n if price < 0:\n raise ValueError('Price cannot be negative')\n self.price = price\n\n def is_available(self):\n return self.in_stock == True\n\n\nclass ProductCatalog:\n def __init__(self):\n self.__items = []\n\n def add(self, product):\n self.__items.append(product)\n\n def find_by_category(self, category):\n result = []\n for p in self.__items:\n if p.category == category:\n result.append(p)\n return result\n```",
|
|
19
|
+
"expectations": [
|
|
20
|
+
"Identifies that `Product` is a plain data holder and recommends converting it to a `@dataclass` — eliminates the boilerplate `__init__`, provides automatic `__repr__` and `__eq__`, and makes the data-holder intent explicit (Pythonic class design; Items 37–43: class and interface guidelines)",
|
|
21
|
+
"Flags `get_name()` as a Java-style getter; Pythonic code uses direct attribute access (Item 44: use plain attributes over getter/setter methods)",
|
|
22
|
+
"Flags `set_price()` as a Java-style setter; recommends replacing with `@property` with a setter that includes the validation (Item 44)",
|
|
23
|
+
"Flags `self.in_stock == True` comparison; should be `return self.in_stock` (Item 2: Follow the PEP 8 Style Guide — never compare to True with ==)",
|
|
24
|
+
"Flags `self.__items` name mangling via double underscore; recommends single underscore `_items` for internal use (Item 42: prefer public attributes)",
|
|
25
|
+
"Notes absence of `__repr__` on `Product` making debugging harder; a dataclass provides this automatically",
|
|
26
|
+
"Identifies the manual list-building loop in `find_by_category` as a candidate for a list comprehension (Item 27)",
|
|
27
|
+
"Provides a corrected version using `@dataclass` with `@property` for price validation"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "eval-03-idiomatic-python-already-good",
|
|
32
|
+
"prompt": "Review this Python code:\n\n```python\nfrom contextlib import contextmanager\nfrom typing import Generator, Iterator\nimport logging\n\nlogger = logging.getLogger(__name__)\n\n\n@contextmanager\ndef managed_connection(host: str, port: int) -> Generator:\n \"\"\"Open a connection and ensure it is closed on exit.\"\"\"\n conn = _connect(host, port)\n try:\n logger.debug('Opened connection to %s:%d', host, port)\n yield conn\n finally:\n conn.close()\n logger.debug('Closed connection to %s:%d', host, port)\n\n\ndef read_records(filepath: str) -> Iterator[dict]:\n \"\"\"Yield records from a newline-delimited JSON file one at a time.\"\"\"\n with open(filepath, encoding='utf-8') as fh:\n for line in fh:\n line = line.strip()\n if line:\n yield _parse(line)\n\n\ndef process_batch(records: Iterator[dict], batch_size: int = 500) -> Iterator[list]:\n \"\"\"Yield successive fixed-size batches from an iterator.\"\"\"\n batch: list[dict] = []\n for record in records:\n batch.append(record)\n if len(batch) >= batch_size:\n yield batch\n batch = []\n if batch:\n yield batch\n```",
|
|
33
|
+
"expectations": [
|
|
34
|
+
"Recognizes this is already idiomatic, well-structured Python and says so explicitly",
|
|
35
|
+
"Praises the use of `@contextmanager` for resource management (Item 66: use contextlib for reusable try/finally patterns)",
|
|
36
|
+
"Praises the generator function `read_records` for memory-efficient file reading (Item 30: consider generators instead of returning lists)",
|
|
37
|
+
"Praises the generator function `process_batch` for lazy batch production without loading the entire sequence into memory (Item 30)",
|
|
38
|
+
"Praises type annotations and docstrings on all public functions (Item 84: Write docstrings for all public APIs)",
|
|
39
|
+
"Does NOT manufacture issues to appear thorough; any suggestions are explicitly framed as minor optional improvements",
|
|
40
|
+
"May note minor optional suggestions such as using `itertools.islice` for batching, but frames them as stylistic alternatives, not violations"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# After
|
|
2
|
+
|
|
3
|
+
Pythonic code with specific exception handling, a dataclass for structure, a comprehension for filtering, and no mutable default argument.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import requests
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from requests.exceptions import HTTPError, ConnectionError, Timeout
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Order:
|
|
12
|
+
id: str
|
|
13
|
+
customer: str
|
|
14
|
+
total: float
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def fetch_orders(api_url: str, filters: dict | None = None) -> list[Order]:
|
|
18
|
+
"""Fetch completed orders from the partner API.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
api_url: Base URL for the orders endpoint.
|
|
22
|
+
filters: Optional extra query parameters. Defaults to empty dict.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
List of Order dataclasses for all completed orders with positive totals.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
HTTPError: If the API returns a non-2xx response.
|
|
29
|
+
ConnectionError: If the API is unreachable.
|
|
30
|
+
Timeout: If the request exceeds the timeout threshold.
|
|
31
|
+
"""
|
|
32
|
+
params = {"status": "completed", **(filters or {})}
|
|
33
|
+
|
|
34
|
+
response = requests.get(api_url, params=params, timeout=10)
|
|
35
|
+
response.raise_for_status() # raises HTTPError for 4xx/5xx
|
|
36
|
+
|
|
37
|
+
data = response.json()
|
|
38
|
+
return [
|
|
39
|
+
Order(id=item["id"], customer=item["customer_name"], total=item["total"])
|
|
40
|
+
for item in data["orders"]
|
|
41
|
+
if item["total"] > 0
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def summarize(orders: list[Order]) -> float:
|
|
46
|
+
"""Return the grand total revenue across all orders."""
|
|
47
|
+
return sum(order.total for order in orders)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Key improvements:
|
|
51
|
+
- `filters: dict | None = None` with `filters or {}` fixes the mutable default argument bug (Item 24: Use None as a default for mutable default arguments)
|
|
52
|
+
- Specific exceptions `HTTPError`, `ConnectionError`, `Timeout` replace the bare `except` clause — errors propagate appropriately and `KeyboardInterrupt` is no longer swallowed (Item 65: Handle exceptions specifically)
|
|
53
|
+
- `response.raise_for_status()` replaces silent failure on bad HTTP responses
|
|
54
|
+
- List comprehension with inline `if` replaces the manual `append` loop (Item 27: Use Comprehensions Instead of map and filter)
|
|
55
|
+
- `@dataclass` replaces a raw `dict` for the order structure — typed, readable, and self-documenting (Item 37: Compose Classes Instead of Nesting Many Levels of Built-in Types)
|
|
56
|
+
- `sum(order.total for order in orders)` in `summarize` is a single idiomatic expression, no intermediate list needed
|