@booklib/skills 1.0.0 → 1.2.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.
@@ -0,0 +1,253 @@
1
+ # Chapter 9: Testing and Debugging (Items 77-85)
2
+
3
+ ## Item 77: Isolate Tests from Each Other via setUp, tearDown, setUpModule, etc.
4
+ ```python
5
+ import unittest
6
+
7
+ class DatabaseTestCase(unittest.TestCase):
8
+ @classmethod
9
+ def setUpClass(cls):
10
+ """Run once before all tests in this class."""
11
+ cls.db = create_test_database()
12
+
13
+ @classmethod
14
+ def tearDownClass(cls):
15
+ """Run once after all tests in this class."""
16
+ cls.db.close()
17
+
18
+ def setUp(self):
19
+ """Run before each test method."""
20
+ self.connection = self.db.connect()
21
+ self.transaction = self.connection.begin()
22
+
23
+ def tearDown(self):
24
+ """Run after each test method."""
25
+ self.transaction.rollback()
26
+ self.connection.close()
27
+
28
+ def test_query(self):
29
+ result = self.connection.execute('SELECT 1')
30
+ self.assertEqual(result, 1)
31
+ ```
32
+
33
+ - `setUp`/`tearDown` run for every test method (isolation)
34
+ - `setUpClass`/`tearDownClass` run once per class (expensive setup)
35
+ - `setUpModule`/`tearDownModule` run once per module
36
+ - Always clean up in tearDown (even if test fails)
37
+
38
+ ## Item 78: Use Mocks to Test Code with Complex Dependencies
39
+ ```python
40
+ from unittest.mock import patch, MagicMock, call
41
+
42
+ # Mock a function
43
+ @patch('mymodule.external_api_call')
44
+ def test_process(mock_api):
45
+ mock_api.return_value = {'status': 'ok'}
46
+ result = process_data()
47
+ mock_api.assert_called_once_with(expected_arg)
48
+ assert result == expected_result
49
+
50
+ # Mock an object's method
51
+ def test_with_mock():
52
+ mock_db = MagicMock()
53
+ mock_db.query.return_value = [{'id': 1}]
54
+ service = MyService(db=mock_db)
55
+ result = service.get_items()
56
+ mock_db.query.assert_called_once()
57
+
58
+ # Verify call order
59
+ mock_db.query.assert_has_calls([
60
+ call('SELECT * FROM users'),
61
+ call('SELECT * FROM orders'),
62
+ ])
63
+
64
+ # Use spec for type checking
65
+ mock = MagicMock(spec=RealClass)
66
+ mock.nonexistent_method() # raises AttributeError
67
+ ```
68
+
69
+ - Mock external dependencies (APIs, databases, file systems)
70
+ - Use `@patch` to replace modules/objects during tests
71
+ - Use `spec=RealClass` to catch API mismatches
72
+ - Verify both return values and call patterns
73
+ - Use `side_effect` for exceptions or multiple return values:
74
+ ```python
75
+ mock.side_effect = ValueError('error')
76
+ mock.side_effect = [1, 2, 3] # returns different values each call
77
+ ```
78
+
79
+ ## Item 79: Encapsulate Dependencies to Facilitate Mocking and Testing
80
+ ```python
81
+ # BAD — hard-coded dependency
82
+ class DataProcessor:
83
+ def process(self):
84
+ data = requests.get('https://api.example.com/data').json()
85
+ return transform(data)
86
+
87
+ # GOOD — inject dependency
88
+ class DataProcessor:
89
+ def __init__(self, data_fetcher):
90
+ self._fetcher = data_fetcher
91
+
92
+ def process(self):
93
+ data = self._fetcher.get_data()
94
+ return transform(data)
95
+
96
+ # Easy to test
97
+ class FakeFetcher:
98
+ def get_data(self):
99
+ return {'test': 'data'}
100
+
101
+ processor = DataProcessor(FakeFetcher())
102
+ result = processor.process()
103
+ ```
104
+
105
+ - Dependency injection makes code testable
106
+ - Accept dependencies as constructor or method parameters
107
+ - Use abstract base classes or protocols to define interfaces
108
+ - Fakes/stubs are often clearer than mocks for complex dependencies
109
+
110
+ ## Item 80: Consider Interactive Debugging with pdb
111
+ ```python
112
+ # Drop into debugger at a specific point
113
+ def complex_function(data):
114
+ result = step_one(data)
115
+ breakpoint() # Python 3.7+ (same as pdb.set_trace())
116
+ final = step_two(result)
117
+ return final
118
+ ```
119
+
120
+ **Key pdb commands:**
121
+ - `n` (next) — execute next line
122
+ - `s` (step) — step into function call
123
+ - `c` (continue) — continue execution until next breakpoint
124
+ - `p expr` — print expression
125
+ - `pp expr` — pretty-print expression
126
+ - `l` (list) — show current code context
127
+ - `w` (where) — show call stack
128
+ - `b line` — set breakpoint at line
129
+ - `r` (return) — run until current function returns
130
+ - `q` (quit) — quit debugger
131
+
132
+ - Use `breakpoint()` (Python 3.7+) instead of `import pdb; pdb.set_trace()`
133
+ - Use `PYTHONBREAKPOINT=0` environment variable to disable all breakpoints
134
+ - Use `post_mortem()` to debug after an exception
135
+
136
+ ## Item 81: Use tracemalloc to Understand Memory Usage and Leaks
137
+ ```python
138
+ import tracemalloc
139
+
140
+ tracemalloc.start()
141
+
142
+ # ... run code that uses memory ...
143
+
144
+ snapshot = tracemalloc.take_snapshot()
145
+ top_stats = snapshot.statistics('lineno')
146
+
147
+ print('Top 10 memory allocations:')
148
+ for stat in top_stats[:10]:
149
+ print(stat)
150
+
151
+ # Compare snapshots to find leaks
152
+ snapshot1 = tracemalloc.take_snapshot()
153
+ # ... more code ...
154
+ snapshot2 = tracemalloc.take_snapshot()
155
+ top_stats = snapshot2.compare_to(snapshot1, 'lineno')
156
+ ```
157
+
158
+ - `tracemalloc` tracks where memory was allocated
159
+ - Use snapshot comparison to find memory leaks
160
+ - Shows file and line number of allocations
161
+ - Much more useful than `gc` module for debugging memory issues
162
+
163
+ ## Item 82: Know Where to Find Community-Built Modules
164
+ - PyPI (Python Package Index) is the main repository
165
+ - Use `pip install` to install packages
166
+ - Check package health: last update, stars, downloads, issues
167
+ - Popular packages: requests, flask, django, pandas, numpy, pytest
168
+
169
+ ## Item 83: Use Virtual Environments for Isolated and Reproducible Dependencies
170
+ ```bash
171
+ # Create virtual environment
172
+ python3 -m venv myenv
173
+
174
+ # Activate
175
+ source myenv/bin/activate
176
+
177
+ # Install packages
178
+ pip install flask==2.0.1
179
+
180
+ # Freeze dependencies
181
+ pip freeze > requirements.txt
182
+
183
+ # Recreate environment
184
+ pip install -r requirements.txt
185
+ ```
186
+
187
+ - Always use virtual environments for projects
188
+ - Never install packages globally with `pip`
189
+ - Use `requirements.txt` for reproducible environments
190
+ - Consider `pyproject.toml` and modern tools (poetry, pipenv)
191
+
192
+ ## Item 84: Write Docstrings for Every Module, Class, and Function
193
+ ```python
194
+ """Module docstring: brief description of the module's purpose."""
195
+
196
+ class MyClass:
197
+ """One-line summary of the class.
198
+
199
+ Extended description of the class if needed.
200
+
201
+ Attributes:
202
+ attr1: Description of attr1.
203
+ attr2: Description of attr2.
204
+ """
205
+
206
+ def method(self, arg1: str, arg2: int = 0) -> bool:
207
+ """One-line summary of method.
208
+
209
+ Extended description if needed.
210
+
211
+ Args:
212
+ arg1: Description of arg1.
213
+ arg2: Description of arg2. Defaults to 0.
214
+
215
+ Returns:
216
+ Description of return value.
217
+
218
+ Raises:
219
+ ValueError: When arg1 is empty.
220
+ """
221
+ ```
222
+
223
+ - First line: one-line summary ending with period
224
+ - Blank line, then extended description if needed
225
+ - Document Args, Returns, Raises sections
226
+ - Use Google style or NumPy style consistently
227
+ - Type hints complement but don't replace docstrings
228
+
229
+ ## Item 85: Use Packages to Organize Modules and Provide Stable APIs
230
+ ```python
231
+ # mypackage/__init__.py
232
+ from mypackage.core import (
233
+ PublicClass,
234
+ public_function,
235
+ )
236
+
237
+ __all__ = ['PublicClass', 'public_function']
238
+
239
+ # mypackage/core.py
240
+ class PublicClass:
241
+ ...
242
+
243
+ def public_function():
244
+ ...
245
+
246
+ def _private_helper(): # not exported
247
+ ...
248
+ ```
249
+
250
+ - Use `__init__.py` to define public API
251
+ - Use `__all__` to control `from package import *` behavior
252
+ - Keep internal modules private with `_` prefix
253
+ - Stable API = external code won't break when internals change
@@ -0,0 +1,175 @@
1
+ # Chapter 10: Collaboration (Items 86-90)
2
+
3
+ ## Item 86: Consider Module-Scoped Code to Configure Deployment Environments
4
+ ```python
5
+ # config.py
6
+ import sys
7
+
8
+ # Module-scoped code runs at import time
9
+ if sys.platform == 'win32':
10
+ DB_PATH = r'C:\data\mydb.sqlite'
11
+ else:
12
+ DB_PATH = '/data/mydb.sqlite'
13
+
14
+ # Use environment variables for deployment config
15
+ import os
16
+ ENVIRONMENT = os.environ.get('APP_ENV', 'development')
17
+
18
+ if ENVIRONMENT == 'production':
19
+ DEBUG = False
20
+ DATABASE_URL = os.environ['DATABASE_URL']
21
+ else:
22
+ DEBUG = True
23
+ DATABASE_URL = 'sqlite:///dev.db'
24
+ ```
25
+
26
+ - Module-level code executes once at import time
27
+ - Use it for deployment configuration, feature flags
28
+ - Keep it minimal — complex logic at import time slows startup
29
+ - Prefer environment variables over hardcoded conditions
30
+
31
+ ## Item 87: Define a Root Exception to Insulate Callers from APIs
32
+ ```python
33
+ # mypackage/exceptions.py
34
+ class Error(Exception):
35
+ """Base class for all exceptions in this package."""
36
+
37
+ class InvalidInputError(Error):
38
+ """Raised when input validation fails."""
39
+
40
+ class AuthenticationError(Error):
41
+ """Raised when authentication fails."""
42
+
43
+ class InternalError(Error):
44
+ """Raised for unexpected internal errors."""
45
+
46
+ # Callers can catch all package errors
47
+ try:
48
+ result = mypackage.do_something()
49
+ except mypackage.Error:
50
+ # Catches any error from this package
51
+ logging.exception('Error from mypackage')
52
+ except Exception:
53
+ # Catches unexpected errors (bugs)
54
+ logging.exception('Unexpected error')
55
+ raise
56
+ ```
57
+
58
+ - Define a root `Error` class for your package/module
59
+ - All custom exceptions inherit from it
60
+ - Callers can catch the root to handle all your errors
61
+ - Three levels: root error (known API errors), specific errors, Exception (bugs)
62
+ - This insulates callers from internal changes to your exception hierarchy
63
+
64
+ ## Item 88: Know How to Break Circular Dependencies
65
+ ```python
66
+ # BAD — circular import
67
+ # module_a.py
68
+ from module_b import B
69
+ class A:
70
+ def use_b(self):
71
+ return B()
72
+
73
+ # module_b.py
74
+ from module_a import A # ImportError: circular!
75
+ class B:
76
+ def use_a(self):
77
+ return A()
78
+
79
+ # FIX 1 — import at function call time
80
+ # module_a.py
81
+ class A:
82
+ def use_b(self):
83
+ from module_b import B # lazy import
84
+ return B()
85
+
86
+ # FIX 2 — restructure to remove the cycle
87
+ # Move shared code to a third module
88
+
89
+ # FIX 3 — use import module, not from module import
90
+ import module_b
91
+ class A:
92
+ def use_b(self):
93
+ return module_b.B()
94
+ ```
95
+
96
+ **Strategies (in order of preference):**
97
+ 1. **Restructure** — move shared code to a common module
98
+ 2. **Import at use time** — put import inside the function
99
+ 3. **Import the module** — use `import module` instead of `from module import name`
100
+
101
+ ## Item 89: Consider warnings to Refactor and Migrate Usage
102
+ ```python
103
+ import warnings
104
+
105
+ def old_function():
106
+ """Deprecated: use new_function instead."""
107
+ warnings.warn(
108
+ 'old_function is deprecated, use new_function',
109
+ DeprecationWarning,
110
+ stacklevel=2 # point to caller, not this function
111
+ )
112
+ return new_function()
113
+
114
+ # Callers see:
115
+ # DeprecationWarning: old_function is deprecated, use new_function
116
+ # result = old_function() # <-- points to their code
117
+ ```
118
+
119
+ - Use `DeprecationWarning` for API migrations
120
+ - `stacklevel=2` makes the warning point to the caller's code
121
+ - Use `warnings.filterwarnings` to control warning behavior
122
+ - In tests, use `warnings.catch_warnings` to verify warnings are raised:
123
+ ```python
124
+ with warnings.catch_warnings(record=True) as w:
125
+ warnings.simplefilter('always')
126
+ old_function()
127
+ assert len(w) == 1
128
+ assert issubclass(w[0].category, DeprecationWarning)
129
+ ```
130
+
131
+ ## Item 90: Consider Static Analysis via typing to Obviate Bugs
132
+ ```python
133
+ from typing import List, Dict, Optional, Tuple, Union
134
+ from typing import Protocol # Python 3.8+
135
+
136
+ # Basic type annotations
137
+ def greet(name: str) -> str:
138
+ return f'Hello, {name}'
139
+
140
+ # Collections
141
+ def process(items: List[int]) -> Dict[str, int]:
142
+ return {'total': sum(items)}
143
+
144
+ # Optional (can be None)
145
+ def find(name: str) -> Optional[str]:
146
+ ...
147
+
148
+ # Protocol for structural typing (duck typing)
149
+ class Readable(Protocol):
150
+ def read(self) -> str:
151
+ ...
152
+
153
+ def process_input(source: Readable) -> str:
154
+ return source.read()
155
+
156
+ # Generic classes
157
+ from typing import Generic, TypeVar
158
+ T = TypeVar('T')
159
+
160
+ class Stack(Generic[T]):
161
+ def __init__(self) -> None:
162
+ self._items: List[T] = []
163
+
164
+ def push(self, item: T) -> None:
165
+ self._items.append(item)
166
+
167
+ def pop(self) -> T:
168
+ return self._items.pop()
169
+ ```
170
+
171
+ - Use type annotations for documentation and static analysis
172
+ - Run `mypy` for type checking: `mypy --strict mymodule.py`
173
+ - Use `Protocol` for structural typing (no inheritance needed)
174
+ - Type hints are not enforced at runtime (use `mypy` to check)
175
+ - Modern syntax (Python 3.10+): `list[int]` instead of `List[int]`, `X | Y` instead of `Union[X, Y]`
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "@booklib/skills",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Book knowledge distilled into structured AI skills for Claude Code and other AI assistants",
5
5
  "bin": {
6
6
  "skills": "./bin/skills.js"
7
7
  },
8
- "keywords": ["claude", "claude-code", "ai", "skills", "agent-skills"],
8
+ "keywords": [
9
+ "claude",
10
+ "claude-code",
11
+ "ai",
12
+ "skills",
13
+ "agent-skills"
14
+ ],
9
15
  "license": "MIT",
10
16
  "repository": {
11
17
  "type": "git",