@malamute/ai-rules 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.
Files changed (133) hide show
  1. package/README.md +270 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/.claude/rules/conventions/documentation.md +324 -0
  4. package/configs/_shared/.claude/rules/conventions/git.md +265 -0
  5. package/configs/_shared/.claude/rules/{performance.md → conventions/performance.md} +1 -1
  6. package/configs/_shared/.claude/rules/conventions/principles.md +334 -0
  7. package/configs/_shared/.claude/rules/devops/ci-cd.md +262 -0
  8. package/configs/_shared/.claude/rules/devops/docker.md +275 -0
  9. package/configs/_shared/.claude/rules/devops/nx.md +194 -0
  10. package/configs/_shared/.claude/rules/domain/backend/api-design.md +203 -0
  11. package/configs/_shared/.claude/rules/lang/csharp/async.md +220 -0
  12. package/configs/_shared/.claude/rules/lang/csharp/csharp.md +314 -0
  13. package/configs/_shared/.claude/rules/lang/csharp/linq.md +210 -0
  14. package/configs/_shared/.claude/rules/lang/python/async.md +337 -0
  15. package/configs/_shared/.claude/rules/lang/python/celery.md +476 -0
  16. package/configs/_shared/.claude/rules/lang/python/config.md +339 -0
  17. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/database/sqlalchemy.md +6 -1
  18. package/configs/_shared/.claude/rules/lang/python/deployment.md +523 -0
  19. package/configs/_shared/.claude/rules/lang/python/error-handling.md +330 -0
  20. package/configs/_shared/.claude/rules/lang/python/migrations.md +421 -0
  21. package/configs/_shared/.claude/rules/lang/python/python.md +172 -0
  22. package/configs/_shared/.claude/rules/lang/python/repository.md +383 -0
  23. package/configs/{python/.claude/rules → _shared/.claude/rules/lang/python}/testing.md +2 -69
  24. package/configs/_shared/.claude/rules/lang/typescript/async.md +447 -0
  25. package/configs/_shared/.claude/rules/lang/typescript/generics.md +356 -0
  26. package/configs/_shared/.claude/rules/lang/typescript/typescript.md +212 -0
  27. package/configs/_shared/.claude/rules/quality/error-handling.md +48 -0
  28. package/configs/_shared/.claude/rules/quality/logging.md +45 -0
  29. package/configs/_shared/.claude/rules/quality/observability.md +240 -0
  30. package/configs/_shared/.claude/rules/quality/testing-patterns.md +65 -0
  31. package/configs/_shared/.claude/rules/security/secrets-management.md +222 -0
  32. package/configs/_shared/.claude/skills/analysis/explore/SKILL.md +257 -0
  33. package/configs/_shared/.claude/skills/analysis/security-audit/SKILL.md +184 -0
  34. package/configs/_shared/.claude/skills/dev/api-endpoint/SKILL.md +126 -0
  35. package/configs/_shared/.claude/{commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  36. package/configs/_shared/.claude/{commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  37. package/configs/_shared/.claude/{commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  38. package/configs/_shared/.claude/skills/infra/deploy/SKILL.md +139 -0
  39. package/configs/_shared/.claude/skills/infra/docker/SKILL.md +95 -0
  40. package/configs/_shared/.claude/skills/infra/migration/SKILL.md +158 -0
  41. package/configs/_shared/.claude/skills/nx/nx-affected/SKILL.md +72 -0
  42. package/configs/_shared/.claude/skills/nx/nx-lib/SKILL.md +375 -0
  43. package/configs/_shared/CLAUDE.md +52 -149
  44. package/configs/angular/.claude/rules/{components.md → core/components.md} +69 -15
  45. package/configs/angular/.claude/rules/core/resource.md +285 -0
  46. package/configs/angular/.claude/rules/core/signals.md +323 -0
  47. package/configs/angular/.claude/rules/http.md +338 -0
  48. package/configs/angular/.claude/rules/routing.md +291 -0
  49. package/configs/angular/.claude/rules/ssr.md +312 -0
  50. package/configs/angular/.claude/rules/state/signal-store.md +408 -0
  51. package/configs/angular/.claude/rules/{state.md → state/state.md} +2 -2
  52. package/configs/angular/.claude/rules/testing.md +7 -7
  53. package/configs/angular/.claude/rules/ui/aria.md +422 -0
  54. package/configs/angular/.claude/rules/ui/forms.md +424 -0
  55. package/configs/angular/.claude/rules/ui/pipes-directives.md +335 -0
  56. package/configs/angular/.claude/settings.json +1 -0
  57. package/configs/angular/.claude/skills/ngrx-slice/SKILL.md +362 -0
  58. package/configs/angular/.claude/skills/signal-store/SKILL.md +445 -0
  59. package/configs/angular/CLAUDE.md +24 -216
  60. package/configs/dotnet/.claude/rules/background-services.md +552 -0
  61. package/configs/dotnet/.claude/rules/configuration.md +426 -0
  62. package/configs/dotnet/.claude/rules/ddd.md +447 -0
  63. package/configs/dotnet/.claude/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/.claude/rules/mediatr.md +320 -0
  65. package/configs/dotnet/.claude/rules/middleware.md +489 -0
  66. package/configs/dotnet/.claude/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/.claude/rules/validation.md +388 -0
  68. package/configs/dotnet/.claude/settings.json +21 -3
  69. package/configs/dotnet/CLAUDE.md +53 -286
  70. package/configs/fastapi/.claude/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/.claude/rules/dependencies.md +170 -0
  72. package/configs/{python → fastapi}/.claude/rules/fastapi.md +61 -1
  73. package/configs/fastapi/.claude/rules/lifespan.md +274 -0
  74. package/configs/fastapi/.claude/rules/middleware.md +229 -0
  75. package/configs/fastapi/.claude/rules/pydantic.md +433 -0
  76. package/configs/fastapi/.claude/rules/responses.md +251 -0
  77. package/configs/fastapi/.claude/rules/routers.md +202 -0
  78. package/configs/fastapi/.claude/rules/security.md +222 -0
  79. package/configs/fastapi/.claude/rules/testing.md +251 -0
  80. package/configs/fastapi/.claude/rules/websockets.md +298 -0
  81. package/configs/fastapi/.claude/settings.json +33 -0
  82. package/configs/fastapi/CLAUDE.md +144 -0
  83. package/configs/flask/.claude/rules/blueprints.md +208 -0
  84. package/configs/flask/.claude/rules/cli.md +285 -0
  85. package/configs/flask/.claude/rules/configuration.md +281 -0
  86. package/configs/flask/.claude/rules/context.md +238 -0
  87. package/configs/flask/.claude/rules/error-handlers.md +278 -0
  88. package/configs/flask/.claude/rules/extensions.md +278 -0
  89. package/configs/flask/.claude/rules/flask.md +171 -0
  90. package/configs/flask/.claude/rules/marshmallow.md +206 -0
  91. package/configs/flask/.claude/rules/security.md +267 -0
  92. package/configs/flask/.claude/rules/testing.md +284 -0
  93. package/configs/flask/.claude/settings.json +33 -0
  94. package/configs/flask/CLAUDE.md +166 -0
  95. package/configs/nestjs/.claude/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/.claude/rules/filters.md +376 -0
  97. package/configs/nestjs/.claude/rules/interceptors.md +317 -0
  98. package/configs/nestjs/.claude/rules/middleware.md +321 -0
  99. package/configs/nestjs/.claude/rules/modules.md +26 -0
  100. package/configs/nestjs/.claude/rules/pipes.md +351 -0
  101. package/configs/nestjs/.claude/rules/websockets.md +451 -0
  102. package/configs/nestjs/.claude/settings.json +16 -2
  103. package/configs/nestjs/CLAUDE.md +57 -215
  104. package/configs/nextjs/.claude/rules/api-routes.md +358 -0
  105. package/configs/nextjs/.claude/rules/authentication.md +355 -0
  106. package/configs/nextjs/.claude/rules/components.md +52 -0
  107. package/configs/nextjs/.claude/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/.claude/rules/database.md +400 -0
  109. package/configs/nextjs/.claude/rules/middleware.md +303 -0
  110. package/configs/nextjs/.claude/rules/routing.md +324 -0
  111. package/configs/nextjs/.claude/rules/seo.md +350 -0
  112. package/configs/nextjs/.claude/rules/server-actions.md +353 -0
  113. package/configs/nextjs/.claude/rules/state/zustand.md +6 -6
  114. package/configs/nextjs/.claude/settings.json +5 -0
  115. package/configs/nextjs/CLAUDE.md +69 -331
  116. package/package.json +23 -9
  117. package/src/cli.js +220 -0
  118. package/src/config.js +29 -0
  119. package/src/index.js +13 -0
  120. package/src/installer.js +361 -0
  121. package/src/merge.js +116 -0
  122. package/src/tech-config.json +29 -0
  123. package/src/utils.js +96 -0
  124. package/configs/python/.claude/rules/flask.md +0 -332
  125. package/configs/python/.claude/settings.json +0 -18
  126. package/configs/python/CLAUDE.md +0 -273
  127. package/src/install.js +0 -315
  128. /package/configs/_shared/.claude/rules/{accessibility.md → domain/frontend/accessibility.md} +0 -0
  129. /package/configs/_shared/.claude/rules/{security.md → security/security.md} +0 -0
  130. /package/configs/_shared/.claude/skills/{debug → dev/debug}/SKILL.md +0 -0
  131. /package/configs/_shared/.claude/skills/{learning → dev/learning}/SKILL.md +0 -0
  132. /package/configs/_shared/.claude/skills/{spec → dev/spec}/SKILL.md +0 -0
  133. /package/configs/_shared/.claude/skills/{review → git/review}/SKILL.md +0 -0
@@ -0,0 +1,210 @@
1
+ ---
2
+ paths:
3
+ - "**/*.cs"
4
+ ---
5
+
6
+ # LINQ Best Practices
7
+
8
+ ## Method vs Query Syntax
9
+
10
+ ```csharp
11
+ // Method syntax - preferred for simple queries
12
+ var activeUsers = users
13
+ .Where(u => u.IsActive)
14
+ .OrderBy(u => u.Name)
15
+ .ToList();
16
+
17
+ // Query syntax - better for joins and complex queries
18
+ var orderDetails = from order in orders
19
+ join customer in customers
20
+ on order.CustomerId equals customer.Id
21
+ join product in products
22
+ on order.ProductId equals product.Id
23
+ where order.Status == OrderStatus.Completed
24
+ select new
25
+ {
26
+ OrderId = order.Id,
27
+ CustomerName = customer.Name,
28
+ ProductName = product.Name
29
+ };
30
+ ```
31
+
32
+ ## Deferred vs Immediate Execution
33
+
34
+ ```csharp
35
+ // Deferred - query not executed until enumerated
36
+ var query = users.Where(u => u.IsActive); // No DB call yet
37
+
38
+ // Immediate - forces execution
39
+ var list = users.Where(u => u.IsActive).ToList(); // Executes now
40
+ var array = users.Where(u => u.IsActive).ToArray(); // Executes now
41
+ var first = users.First(u => u.IsActive); // Executes now
42
+ var count = users.Count(u => u.IsActive); // Executes now
43
+
44
+ // BAD - multiple enumeration
45
+ IEnumerable<User> users = GetUsers();
46
+ if (users.Any()) // First enumeration
47
+ {
48
+ var first = users.First(); // Second enumeration - might get different data!
49
+ }
50
+
51
+ // GOOD - materialize once
52
+ var users = GetUsers().ToList();
53
+ if (users.Count > 0)
54
+ {
55
+ var first = users[0];
56
+ }
57
+ ```
58
+
59
+ ## Filtering
60
+
61
+ ```csharp
62
+ // Chain Where for readability
63
+ var results = items
64
+ .Where(x => x.IsActive)
65
+ .Where(x => x.Category == "Electronics")
66
+ .Where(x => x.Price > 100);
67
+
68
+ // Use Any/All for existence checks
69
+ if (users.Any(u => u.Role == Role.Admin)) // GOOD
70
+ if (users.Where(u => u.Role == Role.Admin).Count() > 0) // BAD
71
+
72
+ // FirstOrDefault with predicate
73
+ var admin = users.FirstOrDefault(u => u.Role == Role.Admin);
74
+
75
+ // SingleOrDefault when expecting 0 or 1
76
+ var user = users.SingleOrDefault(u => u.Email == email);
77
+ ```
78
+
79
+ ## Projection
80
+
81
+ ```csharp
82
+ // Select for transformation
83
+ var dtos = users.Select(u => new UserDto(u.Id, u.Name));
84
+
85
+ // SelectMany to flatten
86
+ var allOrders = customers.SelectMany(c => c.Orders);
87
+
88
+ // Anonymous types for intermediate results
89
+ var intermediate = users
90
+ .Select(u => new { u.Id, FullName = $"{u.FirstName} {u.LastName}" })
91
+ .Where(x => x.FullName.StartsWith("A"));
92
+ ```
93
+
94
+ ## Grouping
95
+
96
+ ```csharp
97
+ // GroupBy
98
+ var usersByRole = users
99
+ .GroupBy(u => u.Role)
100
+ .Select(g => new
101
+ {
102
+ Role = g.Key,
103
+ Count = g.Count(),
104
+ Users = g.ToList()
105
+ });
106
+
107
+ // ToLookup - immediate execution, allows multiple enumeration
108
+ var lookup = users.ToLookup(u => u.Role);
109
+ var admins = lookup[Role.Admin];
110
+ var managers = lookup[Role.Manager];
111
+ ```
112
+
113
+ ## Aggregation
114
+
115
+ ```csharp
116
+ // Aggregate functions
117
+ var total = orders.Sum(o => o.Amount);
118
+ var average = orders.Average(o => o.Amount);
119
+ var max = orders.Max(o => o.Amount);
120
+ var min = orders.Min(o => o.Amount);
121
+
122
+ // Aggregate for custom accumulation
123
+ var concatenated = names.Aggregate((a, b) => $"{a}, {b}");
124
+
125
+ // With seed value
126
+ var total = items.Aggregate(
127
+ seed: 0m,
128
+ func: (sum, item) => sum + item.Price * item.Quantity);
129
+ ```
130
+
131
+ ## Set Operations
132
+
133
+ ```csharp
134
+ // Distinct
135
+ var uniqueCategories = products.Select(p => p.Category).Distinct();
136
+
137
+ // DistinctBy (C# 10+)
138
+ var uniqueByName = products.DistinctBy(p => p.Name);
139
+
140
+ // Union, Intersect, Except
141
+ var allIds = list1.Select(x => x.Id).Union(list2.Select(x => x.Id));
142
+ var commonIds = list1.Select(x => x.Id).Intersect(list2.Select(x => x.Id));
143
+ var onlyInFirst = list1.Select(x => x.Id).Except(list2.Select(x => x.Id));
144
+ ```
145
+
146
+ ## Ordering
147
+
148
+ ```csharp
149
+ // Multiple sort criteria
150
+ var sorted = users
151
+ .OrderBy(u => u.LastName)
152
+ .ThenBy(u => u.FirstName)
153
+ .ThenByDescending(u => u.CreatedAt);
154
+
155
+ // Order vs OrderBy (C# 11+)
156
+ var ordered = items.Order(); // Uses default comparer
157
+ var orderedDesc = items.OrderDescending();
158
+ ```
159
+
160
+ ## Pagination
161
+
162
+ ```csharp
163
+ // Skip/Take for pagination
164
+ var page = users
165
+ .OrderBy(u => u.Id)
166
+ .Skip((pageNumber - 1) * pageSize)
167
+ .Take(pageSize)
168
+ .ToList();
169
+
170
+ // Chunk for batching (C# 10+)
171
+ var batches = items.Chunk(100);
172
+ foreach (var batch in batches)
173
+ {
174
+ await ProcessBatchAsync(batch);
175
+ }
176
+ ```
177
+
178
+ ## EF Core Specific
179
+
180
+ ```csharp
181
+ // GOOD - let EF translate to SQL
182
+ var users = await _context.Users
183
+ .Where(u => u.IsActive)
184
+ .OrderBy(u => u.Name)
185
+ .ToListAsync();
186
+
187
+ // BAD - client-side evaluation (throws in EF Core 3+)
188
+ var users = await _context.Users
189
+ .Where(u => SomeLocalMethod(u)) // Can't translate
190
+ .ToListAsync();
191
+
192
+ // GOOD - explicit client evaluation when needed
193
+ var users = await _context.Users
194
+ .Where(u => u.IsActive)
195
+ .AsEnumerable() // Switch to client
196
+ .Where(u => SomeLocalMethod(u))
197
+ .ToList();
198
+
199
+ // Use AsNoTracking for read-only queries
200
+ var users = await _context.Users
201
+ .AsNoTracking()
202
+ .Where(u => u.IsActive)
203
+ .ToListAsync();
204
+
205
+ // Projection to avoid over-fetching
206
+ var dtos = await _context.Users
207
+ .Where(u => u.IsActive)
208
+ .Select(u => new UserDto(u.Id, u.Name, u.Email))
209
+ .ToListAsync();
210
+ ```
@@ -0,0 +1,337 @@
1
+ ---
2
+ paths:
3
+ - "**/handlers/**/*.py"
4
+ - "**/services/**/*.py"
5
+ - "**/consumers/**/*.py"
6
+ - "**/workers/**/*.py"
7
+ - "**/*_async*.py"
8
+ ---
9
+
10
+ # Python Async Patterns
11
+
12
+ ## Async Function Basics
13
+
14
+ ```python
15
+ import asyncio
16
+ from typing import AsyncIterator
17
+
18
+ # Always use async def for I/O operations
19
+ async def fetch_user(user_id: int) -> User:
20
+ return await db.users.get(user_id)
21
+
22
+ # Never mix sync I/O in async functions
23
+ # BAD
24
+ async def bad_fetch():
25
+ return requests.get(url) # Blocks event loop!
26
+
27
+ # GOOD
28
+ async def good_fetch():
29
+ async with httpx.AsyncClient() as client:
30
+ return await client.get(url)
31
+ ```
32
+
33
+ ## Concurrent Execution
34
+
35
+ ```python
36
+ import asyncio
37
+
38
+ # Run tasks concurrently
39
+ async def fetch_all_data():
40
+ # Wrong - sequential execution
41
+ users = await fetch_users()
42
+ orders = await fetch_orders()
43
+ products = await fetch_products()
44
+
45
+ # Right - concurrent execution
46
+ users, orders, products = await asyncio.gather(
47
+ fetch_users(),
48
+ fetch_orders(),
49
+ fetch_products(),
50
+ )
51
+
52
+ return users, orders, products
53
+
54
+ # With error handling
55
+ async def fetch_with_errors():
56
+ results = await asyncio.gather(
57
+ fetch_users(),
58
+ fetch_orders(),
59
+ fetch_products(),
60
+ return_exceptions=True, # Don't fail on first error
61
+ )
62
+
63
+ for result in results:
64
+ if isinstance(result, Exception):
65
+ logger.error(f"Task failed: {result}")
66
+ ```
67
+
68
+ ## TaskGroup (Python 3.11+)
69
+
70
+ ```python
71
+ async def process_items(items: list[Item]) -> list[Result]:
72
+ results = []
73
+
74
+ async with asyncio.TaskGroup() as tg:
75
+ for item in items:
76
+ tg.create_task(process_item(item))
77
+
78
+ # All tasks complete when exiting context
79
+ return results
80
+
81
+ # With exception handling
82
+ async def process_with_handling(items: list[Item]):
83
+ try:
84
+ async with asyncio.TaskGroup() as tg:
85
+ for item in items:
86
+ tg.create_task(process_item(item))
87
+ except* ValueError as eg:
88
+ for exc in eg.exceptions:
89
+ logger.error(f"Validation error: {exc}")
90
+ except* ConnectionError as eg:
91
+ for exc in eg.exceptions:
92
+ logger.error(f"Connection error: {exc}")
93
+ ```
94
+
95
+ ## Async Context Managers
96
+
97
+ ```python
98
+ from contextlib import asynccontextmanager
99
+
100
+ @asynccontextmanager
101
+ async def get_db_connection():
102
+ conn = await create_connection()
103
+ try:
104
+ yield conn
105
+ finally:
106
+ await conn.close()
107
+
108
+ # Usage
109
+ async def query_users():
110
+ async with get_db_connection() as conn:
111
+ return await conn.fetch("SELECT * FROM users")
112
+
113
+ # Class-based context manager
114
+ class AsyncDatabaseSession:
115
+ async def __aenter__(self):
116
+ self.session = await create_session()
117
+ return self.session
118
+
119
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
120
+ if exc_type:
121
+ await self.session.rollback()
122
+ else:
123
+ await self.session.commit()
124
+ await self.session.close()
125
+ ```
126
+
127
+ ## Async Iterators
128
+
129
+ ```python
130
+ from typing import AsyncIterator
131
+
132
+ async def fetch_pages(url: str) -> AsyncIterator[Page]:
133
+ next_url = url
134
+ while next_url:
135
+ response = await fetch(next_url)
136
+ yield response.data
137
+ next_url = response.next_url
138
+
139
+ # Usage
140
+ async def process_all_pages():
141
+ async for page in fetch_pages("/api/items"):
142
+ for item in page.items:
143
+ await process_item(item)
144
+
145
+ # Async comprehension
146
+ async def get_all_items():
147
+ return [
148
+ item
149
+ async for page in fetch_pages("/api/items")
150
+ for item in page.items
151
+ ]
152
+ ```
153
+
154
+ ## Semaphore for Rate Limiting
155
+
156
+ ```python
157
+ import asyncio
158
+
159
+ async def fetch_with_limit(urls: list[str], max_concurrent: int = 10):
160
+ semaphore = asyncio.Semaphore(max_concurrent)
161
+
162
+ async def fetch_one(url: str):
163
+ async with semaphore:
164
+ async with httpx.AsyncClient() as client:
165
+ return await client.get(url)
166
+
167
+ return await asyncio.gather(*[fetch_one(url) for url in urls])
168
+ ```
169
+
170
+ ## Timeouts
171
+
172
+ ```python
173
+ import asyncio
174
+
175
+ async def fetch_with_timeout(url: str, timeout: float = 10.0):
176
+ try:
177
+ async with asyncio.timeout(timeout):
178
+ return await fetch(url)
179
+ except asyncio.TimeoutError:
180
+ logger.error(f"Timeout fetching {url}")
181
+ raise
182
+
183
+ # Or with wait_for (older style)
184
+ async def fetch_with_wait_for(url: str):
185
+ try:
186
+ return await asyncio.wait_for(fetch(url), timeout=10.0)
187
+ except asyncio.TimeoutError:
188
+ raise
189
+ ```
190
+
191
+ ## Background Tasks
192
+
193
+ ```python
194
+ import asyncio
195
+ from collections.abc import Callable
196
+
197
+ class BackgroundTasks:
198
+ def __init__(self):
199
+ self._tasks: set[asyncio.Task] = set()
200
+
201
+ def add_task(self, coro):
202
+ task = asyncio.create_task(coro)
203
+ self._tasks.add(task)
204
+ task.add_done_callback(self._tasks.discard)
205
+
206
+ async def shutdown(self):
207
+ for task in self._tasks:
208
+ task.cancel()
209
+ await asyncio.gather(*self._tasks, return_exceptions=True)
210
+
211
+ # Usage in FastAPI
212
+ background = BackgroundTasks()
213
+
214
+ @app.post("/orders")
215
+ async def create_order(order: Order):
216
+ saved = await save_order(order)
217
+ background.add_task(send_notification(order))
218
+ return saved
219
+
220
+ @app.on_event("shutdown")
221
+ async def shutdown():
222
+ await background.shutdown()
223
+ ```
224
+
225
+ ## Async Queue
226
+
227
+ ```python
228
+ import asyncio
229
+
230
+ async def producer(queue: asyncio.Queue[int]):
231
+ for i in range(10):
232
+ await queue.put(i)
233
+ print(f"Produced: {i}")
234
+ await asyncio.sleep(0.1)
235
+ await queue.put(None) # Sentinel to stop
236
+
237
+ async def consumer(queue: asyncio.Queue[int]):
238
+ while True:
239
+ item = await queue.get()
240
+ if item is None:
241
+ break
242
+ print(f"Consumed: {item}")
243
+ queue.task_done()
244
+
245
+ async def main():
246
+ queue: asyncio.Queue[int] = asyncio.Queue(maxsize=5)
247
+
248
+ await asyncio.gather(
249
+ producer(queue),
250
+ consumer(queue),
251
+ )
252
+ ```
253
+
254
+ ## Async Lock
255
+
256
+ ```python
257
+ import asyncio
258
+
259
+ class Counter:
260
+ def __init__(self):
261
+ self._value = 0
262
+ self._lock = asyncio.Lock()
263
+
264
+ async def increment(self):
265
+ async with self._lock:
266
+ self._value += 1
267
+ return self._value
268
+
269
+ async def get(self) -> int:
270
+ async with self._lock:
271
+ return self._value
272
+ ```
273
+
274
+ ## SQLAlchemy Async
275
+
276
+ ```python
277
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
278
+ from sqlalchemy.orm import sessionmaker
279
+
280
+ # Create async engine
281
+ engine = create_async_engine(
282
+ "postgresql+asyncpg://user:pass@localhost/db",
283
+ echo=True,
284
+ )
285
+
286
+ # Async session factory
287
+ async_session = sessionmaker(
288
+ engine,
289
+ class_=AsyncSession,
290
+ expire_on_commit=False,
291
+ )
292
+
293
+ # Usage
294
+ async def get_user(user_id: int) -> User | None:
295
+ async with async_session() as session:
296
+ result = await session.execute(
297
+ select(User).where(User.id == user_id)
298
+ )
299
+ return result.scalar_one_or_none()
300
+
301
+ # FastAPI dependency
302
+ async def get_db() -> AsyncIterator[AsyncSession]:
303
+ async with async_session() as session:
304
+ try:
305
+ yield session
306
+ await session.commit()
307
+ except Exception:
308
+ await session.rollback()
309
+ raise
310
+ ```
311
+
312
+ ## httpx Async Client
313
+
314
+ ```python
315
+ import httpx
316
+
317
+ # Reuse client for connection pooling
318
+ class ApiClient:
319
+ def __init__(self, base_url: str):
320
+ self._client = httpx.AsyncClient(
321
+ base_url=base_url,
322
+ timeout=30.0,
323
+ headers={"User-Agent": "MyApp/1.0"},
324
+ )
325
+
326
+ async def get(self, path: str) -> dict:
327
+ response = await self._client.get(path)
328
+ response.raise_for_status()
329
+ return response.json()
330
+
331
+ async def close(self):
332
+ await self._client.aclose()
333
+
334
+ # Usage with context manager
335
+ async with httpx.AsyncClient() as client:
336
+ response = await client.get("https://api.example.com/data")
337
+ ```