aury-boot 0.0.10__py3-none-any.whl → 0.0.12__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.
aury/boot/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.10'
32
- __version_tuple__ = version_tuple = (0, 0, 10)
31
+ __version__ = version = '0.0.12'
32
+ __version_tuple__ = version_tuple = (0, 0, 12)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -117,6 +117,8 @@ def run_migrations_offline() -> None:
117
117
  dialect_opts={{"paramstyle": "named"}},
118
118
  compare_type=True,
119
119
  compare_server_default=True,
120
+ # 启用 batch 模式以更好地支持 SQLite 等不完整 DDL 的后端
121
+ render_as_batch=True,
120
122
  )
121
123
  with context.begin_transaction():
122
124
  context.run_migrations()
@@ -129,6 +131,8 @@ def _do_run_migrations(connection) -> None:
129
131
  target_metadata=target_metadata,
130
132
  compare_type=True,
131
133
  compare_server_default=True,
134
+ # 启用 batch 模式以更好地支持 SQLite 等不完整 DDL 的后端
135
+ render_as_batch=True,
132
136
  )
133
137
  with context.begin_transaction():
134
138
  context.run_migrations()
aury/boot/commands/app.py CHANGED
@@ -86,12 +86,14 @@ def _get_app() -> typer.Typer:
86
86
  from .generate import app as generate_app
87
87
  from .init import init
88
88
  from .migrate import app as migrate_app
89
+ from .pkg import app as pkg_app
89
90
  from .scheduler import app as scheduler_app
90
91
  from .server import app as server_app
91
92
  from .worker import app as worker_app
92
93
 
93
94
  app.command(name="init", help="🎯 初始化项目脚手架")(init)
94
95
  app.add_typer(add_app, name="add", help="➕ 添加可选模块")
96
+ app.add_typer(pkg_app, name="pkg", help="📦 包管理")
95
97
  app.add_typer(generate_app, name="generate", help="⚡ 代码生成器")
96
98
  app.add_typer(server_app, name="server", help="🖥️ 服务器管理")
97
99
  app.add_typer(scheduler_app, name="scheduler", help="🕐 独立运行调度器")
@@ -162,6 +164,10 @@ def register_commands(
162
164
  from .add import app as add_app
163
165
  target_app.add_typer(add_app, name="add", help="➕ 添加可选模块")
164
166
 
167
+ # pkg 命令始终注册(包管理是通用功能)
168
+ from .pkg import app as pkg_app
169
+ target_app.add_typer(pkg_app, name="pkg", help="📦 包管理")
170
+
165
171
  if include_generate:
166
172
  from .generate import app as generate_app
167
173
  target_app.add_typer(generate_app, name="generate", help="⚡ 代码生成器")
@@ -205,11 +211,12 @@ def get_command_modules() -> dict[str, type]:
205
211
  # {'init': <module>, 'add': <module>, 'server': <module>, ...}
206
212
  ```
207
213
  """
208
- from . import add, docker, docs, generate, init, migrate, scheduler, server, worker
214
+ from . import add, docker, docs, generate, init, migrate, pkg, scheduler, server, worker
209
215
 
210
216
  return {
211
217
  "init": init,
212
218
  "add": add,
219
+ "pkg": pkg,
213
220
  "generate": generate,
214
221
  "server": server,
215
222
  "scheduler": scheduler,
@@ -928,6 +928,12 @@ def init(
928
928
  console.print(" 4. 访问 API 文档:")
929
929
  console.print(" [cyan]http://127.0.0.1:8000/docs[/cyan]")
930
930
  console.print()
931
+ console.print("[bold]包管理(按需安装可选模块):[/bold]")
932
+ console.print(" [cyan]aury pkg list[/cyan] # 查看所有可用模块")
933
+ console.print(" [cyan]aury pkg preset[/cyan] # 查看预设配置")
934
+ console.print(" [cyan]aury pkg install postgres redis[/cyan] # 安装指定模块")
935
+ console.print(" [cyan]aury pkg install --preset api[/cyan] # 按预设安装")
936
+ console.print()
931
937
  console.print("[bold]常用命令:[/bold]")
932
938
  console.print(" [cyan]aury generate crud user -i[/cyan] # 生成 CRUD(交互式)")
933
939
  console.print(" [cyan]aury generate model user -i[/cyan] # 生成模型(交互式)")
@@ -0,0 +1,471 @@
1
+ """包管理命令。
2
+
3
+ 用法:
4
+ aury pkg list # 列出所有可安装模块
5
+ aury pkg list --installed # 已安装的模块
6
+ aury pkg preset # 列出预设
7
+ aury pkg preset api # 查看预设详情
8
+ aury pkg install postgres redis # 安装模块
9
+ aury pkg install --preset api # 按预设安装
10
+ aury pkg remove redis # 卸载模块
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass
16
+ from enum import Enum
17
+ import subprocess
18
+ import sys
19
+ from typing import Annotated
20
+
21
+ from rich.console import Console
22
+ from rich.table import Table
23
+ import typer
24
+
25
+ console = Console()
26
+
27
+ app = typer.Typer(
28
+ name="pkg",
29
+ help="包管理 - 安装/卸载 aury 生态模块",
30
+ no_args_is_help=True,
31
+ )
32
+
33
+
34
+ # ============================================================================
35
+ # 数据定义
36
+ # ============================================================================
37
+
38
+
39
+ class Category(str, Enum):
40
+ """模块分类。"""
41
+
42
+ DATABASE = "database"
43
+ CACHE = "cache"
44
+ TASK = "task"
45
+ SCHEDULER = "scheduler"
46
+ ADMIN = "admin"
47
+ STORAGE = "storage"
48
+ ECOSYSTEM = "ecosystem"
49
+
50
+
51
+ @dataclass
52
+ class ModuleInfo:
53
+ """模块信息。"""
54
+
55
+ name: str
56
+ desc: str
57
+ usage: str
58
+ category: Category
59
+ deps: list[str] # extras 的依赖包名
60
+ is_extra: bool = True # True=extras, False=生态包
61
+ pkg: str | None = None # 生态包的完整包名
62
+
63
+
64
+ # Extras(aury-boot 的可选依赖)
65
+ MODULES: dict[str, ModuleInfo] = {
66
+ # 数据库驱动
67
+ "postgres": ModuleInfo(
68
+ name="postgres",
69
+ desc="PostgreSQL 异步驱动",
70
+ usage="DatabaseManager 使用 PostgreSQL 时需要",
71
+ category=Category.DATABASE,
72
+ deps=["asyncpg"],
73
+ ),
74
+ "mysql": ModuleInfo(
75
+ name="mysql",
76
+ desc="MySQL 异步驱动",
77
+ usage="DatabaseManager 使用 MySQL 时需要",
78
+ category=Category.DATABASE,
79
+ deps=["aiomysql"],
80
+ ),
81
+ "sqlite": ModuleInfo(
82
+ name="sqlite",
83
+ desc="SQLite 异步驱动",
84
+ usage="DatabaseManager 使用 SQLite 时需要(本地开发推荐)",
85
+ category=Category.DATABASE,
86
+ deps=["aiosqlite"],
87
+ ),
88
+ # 缓存
89
+ "redis": ModuleInfo(
90
+ name="redis",
91
+ desc="Redis 客户端",
92
+ usage="CacheManager 使用 Redis 后端时需要",
93
+ category=Category.CACHE,
94
+ deps=["redis"],
95
+ ),
96
+ # 任务队列
97
+ "tasks": ModuleInfo(
98
+ name="tasks",
99
+ desc="Dramatiq 任务队列",
100
+ usage="TaskManager 异步任务时需要",
101
+ category=Category.TASK,
102
+ deps=["dramatiq", "kombu", "dramatiq-kombu-broker"],
103
+ ),
104
+ "rabbitmq": ModuleInfo(
105
+ name="rabbitmq",
106
+ desc="RabbitMQ 消息队列后端",
107
+ usage="TaskManager/EventBus 使用 RabbitMQ 时需要(需配合 tasks)",
108
+ category=Category.TASK,
109
+ deps=["amqp"],
110
+ ),
111
+ # 调度器
112
+ "scheduler": ModuleInfo(
113
+ name="scheduler",
114
+ desc="APScheduler 定时调度",
115
+ usage="SchedulerManager 定时任务时需要",
116
+ category=Category.SCHEDULER,
117
+ deps=["apscheduler"],
118
+ ),
119
+ # 管理后台
120
+ "admin": ModuleInfo(
121
+ name="admin",
122
+ desc="SQLAdmin 管理后台",
123
+ usage="启用 /admin 管理界面时需要",
124
+ category=Category.ADMIN,
125
+ deps=["sqladmin", "itsdangerous"],
126
+ ),
127
+ # 存储(extras)
128
+ "s3": ModuleInfo(
129
+ name="s3",
130
+ desc="S3 兼容存储(AWS/MinIO/OSS)",
131
+ usage="StorageManager 使用 S3 兼容存储时需要",
132
+ category=Category.STORAGE,
133
+ deps=["aury-sdk-storage[aws]"],
134
+ ),
135
+ # 生态包
136
+ "storage-aws": ModuleInfo(
137
+ name="storage-aws",
138
+ desc="AWS S3 兼容存储",
139
+ usage="StorageManager 使用 AWS S3/MinIO/OSS 时需要",
140
+ category=Category.ECOSYSTEM,
141
+ deps=[],
142
+ is_extra=False,
143
+ pkg="aury-sdk-storage[aws]",
144
+ ),
145
+ "storage-cos": ModuleInfo(
146
+ name="storage-cos",
147
+ desc="腾讯云 COS 原生存储",
148
+ usage="StorageManager 使用腾讯云 COS 时推荐(性能更好)",
149
+ category=Category.ECOSYSTEM,
150
+ deps=[],
151
+ is_extra=False,
152
+ pkg="aury-sdk-storage[cos]",
153
+ ),
154
+ }
155
+
156
+
157
+ @dataclass
158
+ class PresetInfo:
159
+ """预设信息。"""
160
+
161
+ name: str
162
+ desc: str
163
+ modules: list[str]
164
+
165
+
166
+ PRESETS: dict[str, PresetInfo] = {
167
+ "minimal": PresetInfo(
168
+ name="minimal",
169
+ desc="最小化(本地开发/测试)",
170
+ modules=["sqlite"],
171
+ ),
172
+ "api": PresetInfo(
173
+ name="api",
174
+ desc="API 服务(Web 接口 + 管理后台)",
175
+ modules=["postgres", "redis", "admin"],
176
+ ),
177
+ "worker": PresetInfo(
178
+ name="worker",
179
+ desc="后台 Worker(任务队列 + 调度器)",
180
+ modules=["postgres", "redis", "tasks", "rabbitmq", "scheduler"],
181
+ ),
182
+ "full": PresetInfo(
183
+ name="full",
184
+ desc="完整功能(所有模块)",
185
+ modules=["postgres", "redis", "tasks", "rabbitmq", "scheduler", "admin", "storage-cos"],
186
+ ),
187
+ }
188
+
189
+
190
+ # 分类显示名称
191
+ CATEGORY_NAMES: dict[Category, str] = {
192
+ Category.DATABASE: "📦 数据库驱动",
193
+ Category.CACHE: "📦 缓存",
194
+ Category.TASK: "📦 任务队列",
195
+ Category.SCHEDULER: "📦 定时调度",
196
+ Category.ADMIN: "📦 管理后台",
197
+ Category.STORAGE: "📦 对象存储",
198
+ Category.ECOSYSTEM: "🌐 生态包",
199
+ }
200
+
201
+
202
+ # ============================================================================
203
+ # 工具函数
204
+ # ============================================================================
205
+
206
+
207
+ def _get_installed_packages() -> set[str]:
208
+ """获取已安装的包名。"""
209
+ try:
210
+ result = subprocess.run(
211
+ [sys.executable, "-m", "pip", "list", "--format=freeze"],
212
+ capture_output=True,
213
+ text=True,
214
+ check=True,
215
+ )
216
+ installed = set()
217
+ for line in result.stdout.strip().split("\n"):
218
+ if "==" in line:
219
+ pkg_name = line.split("==")[0].lower().replace("-", "_")
220
+ installed.add(pkg_name)
221
+ return installed
222
+ except subprocess.CalledProcessError:
223
+ return set()
224
+
225
+
226
+ def _is_module_installed(module: ModuleInfo) -> bool:
227
+ """检查模块是否已安装。"""
228
+ installed = _get_installed_packages()
229
+
230
+ if module.is_extra:
231
+ # 检查 deps 中的包是否已安装
232
+ for dep in module.deps:
233
+ # 处理 extras 语法,如 aury-sdk-storage[aws]
234
+ pkg_name = dep.split("[")[0].lower().replace("-", "_")
235
+ if pkg_name not in installed:
236
+ return False
237
+ return bool(module.deps)
238
+ else:
239
+ # 生态包:检查包名
240
+ if module.pkg:
241
+ pkg_name = module.pkg.split("[")[0].lower().replace("-", "_")
242
+ return pkg_name in installed
243
+ return False
244
+
245
+
246
+ def _run_uv_command(args: list[str]) -> bool:
247
+ """运行 uv 命令。"""
248
+ cmd = ["uv", *args]
249
+ console.print(f"[dim]$ {' '.join(cmd)}[/dim]")
250
+ try:
251
+ subprocess.run(cmd, check=True)
252
+ return True
253
+ except subprocess.CalledProcessError as e:
254
+ console.print(f"[red]命令失败: {e}[/red]")
255
+ return False
256
+ except FileNotFoundError:
257
+ console.print("[red]错误: 未找到 uv,请先安装: pip install uv[/red]")
258
+ return False
259
+
260
+
261
+ # ============================================================================
262
+ # 命令实现
263
+ # ============================================================================
264
+
265
+
266
+ @app.command(name="list")
267
+ def list_modules(
268
+ installed: Annotated[
269
+ bool,
270
+ typer.Option("--installed", "-i", help="仅显示已安装的模块"),
271
+ ] = False,
272
+ ) -> None:
273
+ """列出所有可安装的模块。"""
274
+ installed_pkgs = _get_installed_packages() if installed else None
275
+
276
+ # 按分类组织
277
+ by_category: dict[Category, list[ModuleInfo]] = {}
278
+ for module in MODULES.values():
279
+ if module.category not in by_category:
280
+ by_category[module.category] = []
281
+ by_category[module.category].append(module)
282
+
283
+ # 输出
284
+ for category in Category:
285
+ modules = by_category.get(category, [])
286
+ if not modules:
287
+ continue
288
+
289
+ # 过滤已安装
290
+ if installed:
291
+ modules = [m for m in modules if _is_module_installed(m)]
292
+ if not modules:
293
+ continue
294
+
295
+ console.print()
296
+ console.print(f"[bold]{CATEGORY_NAMES[category]}[/bold]")
297
+
298
+ table = Table(show_header=False, box=None, padding=(0, 2))
299
+ table.add_column("名称", style="cyan", width=15)
300
+ table.add_column("描述", width=30)
301
+ table.add_column("用途", style="dim")
302
+
303
+ for module in modules:
304
+ status = ""
305
+ if installed_pkgs is not None:
306
+ is_installed = _is_module_installed(module)
307
+ status = " [green]✓[/green]" if is_installed else ""
308
+ table.add_row(
309
+ f"{module.name}{status}",
310
+ module.desc,
311
+ f"→ {module.usage}",
312
+ )
313
+
314
+ console.print(table)
315
+
316
+ console.print()
317
+ console.print("[dim]提示: 使用 aury pkg install <模块名> 安装模块[/dim]")
318
+
319
+
320
+ @app.command(name="preset")
321
+ def list_presets(
322
+ name: Annotated[
323
+ str | None,
324
+ typer.Argument(help="预设名称(留空列出所有预设)"),
325
+ ] = None,
326
+ ) -> None:
327
+ """查看预设配置。"""
328
+ if name is None:
329
+ # 列出所有预设
330
+ console.print()
331
+ console.print("[bold]📋 可用预设[/bold]")
332
+ console.print()
333
+
334
+ table = Table(show_header=True, box=None, padding=(0, 2))
335
+ table.add_column("预设", style="cyan", width=12)
336
+ table.add_column("描述", width=35)
337
+ table.add_column("包含模块", style="dim")
338
+
339
+ for preset in PRESETS.values():
340
+ table.add_row(
341
+ preset.name,
342
+ preset.desc,
343
+ ", ".join(preset.modules),
344
+ )
345
+
346
+ console.print(table)
347
+ console.print()
348
+ console.print("[dim]提示: 使用 aury pkg preset <预设名> 查看详情[/dim]")
349
+ console.print("[dim] 使用 aury pkg install --preset <预设名> 安装[/dim]")
350
+ else:
351
+ # 查看指定预设
352
+ if name not in PRESETS:
353
+ console.print(f"[red]错误: 预设 '{name}' 不存在[/red]")
354
+ console.print(f"[dim]可用预设: {', '.join(PRESETS.keys())}[/dim]")
355
+ raise typer.Exit(1)
356
+
357
+ preset = PRESETS[name]
358
+ console.print()
359
+ console.print(f"[bold]📋 预设: {preset.name}[/bold]")
360
+ console.print(f"[dim]{preset.desc}[/dim]")
361
+ console.print()
362
+
363
+ console.print("[bold]包含模块:[/bold]")
364
+ for module_name in preset.modules:
365
+ module = MODULES.get(module_name)
366
+ if module:
367
+ installed = _is_module_installed(module)
368
+ status = "[green]✓ 已安装[/green]" if installed else "[dim]未安装[/dim]"
369
+ console.print(f" • {module.name}: {module.desc} {status}")
370
+ else:
371
+ console.print(f" • {module_name} [red](未知模块)[/red]")
372
+
373
+ console.print()
374
+ console.print(f"[dim]安装命令: aury pkg install --preset {name}[/dim]")
375
+
376
+
377
+ @app.command(name="install")
378
+ def install_modules(
379
+ modules: Annotated[
380
+ list[str] | None,
381
+ typer.Argument(help="要安装的模块名称"),
382
+ ] = None,
383
+ preset: Annotated[
384
+ str | None,
385
+ typer.Option("--preset", "-p", help="使用预设安装"),
386
+ ] = None,
387
+ ) -> None:
388
+ """安装模块。"""
389
+ if preset:
390
+ # 使用预设
391
+ if preset not in PRESETS:
392
+ console.print(f"[red]错误: 预设 '{preset}' 不存在[/red]")
393
+ console.print(f"[dim]可用预设: {', '.join(PRESETS.keys())}[/dim]")
394
+ raise typer.Exit(1)
395
+
396
+ preset_info = PRESETS[preset]
397
+ modules = preset_info.modules
398
+ console.print(f"[bold]📦 安装预设: {preset_info.name}[/bold]")
399
+ console.print(f"[dim]{preset_info.desc}[/dim]")
400
+ console.print()
401
+
402
+ if not modules:
403
+ console.print("[red]错误: 请指定要安装的模块,或使用 --preset[/red]")
404
+ raise typer.Exit(1)
405
+
406
+ # 收集要安装的包
407
+ extras_to_install: list[str] = []
408
+ pkgs_to_install: list[str] = []
409
+
410
+ for module_name in modules:
411
+ if module_name not in MODULES:
412
+ console.print(f"[yellow]警告: 模块 '{module_name}' 不存在,跳过[/yellow]")
413
+ continue
414
+
415
+ module = MODULES[module_name]
416
+ if module.is_extra:
417
+ extras_to_install.append(module.name)
418
+ else:
419
+ if module.pkg:
420
+ pkgs_to_install.append(module.pkg)
421
+
422
+ # 安装 extras
423
+ if extras_to_install:
424
+ extras_str = ",".join(extras_to_install)
425
+ console.print(f"[bold]安装 extras: {extras_str}[/bold]")
426
+ if not _run_uv_command(["add", f"aury-boot[{extras_str}]"]):
427
+ raise typer.Exit(1)
428
+
429
+ # 安装生态包
430
+ for pkg in pkgs_to_install:
431
+ console.print(f"[bold]安装生态包: {pkg}[/bold]")
432
+ if not _run_uv_command(["add", pkg]):
433
+ raise typer.Exit(1)
434
+
435
+ console.print()
436
+ console.print("[green]✅ 安装完成[/green]")
437
+
438
+
439
+ @app.command(name="remove")
440
+ def remove_modules(
441
+ modules: Annotated[
442
+ list[str],
443
+ typer.Argument(help="要卸载的模块名称"),
444
+ ],
445
+ ) -> None:
446
+ """卸载模块。"""
447
+ for module_name in modules:
448
+ if module_name not in MODULES:
449
+ console.print(f"[yellow]警告: 模块 '{module_name}' 不存在,跳过[/yellow]")
450
+ continue
451
+
452
+ module = MODULES[module_name]
453
+
454
+ if module.is_extra:
455
+ # extras 需要移除具体的依赖包
456
+ console.print(f"[bold]移除 {module.name} 的依赖...[/bold]")
457
+ for dep in module.deps:
458
+ pkg_name = dep.split("[")[0] # 去掉 extras 语法
459
+ _run_uv_command(["remove", pkg_name])
460
+ else:
461
+ # 生态包直接移除
462
+ if module.pkg:
463
+ pkg_name = module.pkg.split("[")[0]
464
+ console.print(f"[bold]移除生态包: {pkg_name}[/bold]")
465
+ _run_uv_command(["remove", pkg_name])
466
+
467
+ console.print()
468
+ console.print("[green]✅ 卸载完成[/green]")
469
+
470
+
471
+ __all__ = ["app"]
@@ -11,17 +11,20 @@ pip install "aury-sdk-storage[aws]"
11
11
 
12
12
  ## 10.2 基本用法(StorageManager)
13
13
 
14
- `StorageManager` 支持**命名多实例**,可以同时管理多个存储后端。
14
+ `StorageManager` 支持**命名多实例**,内部使用 `aury-sdk-storage` 提供的 `StorageFactory.from_config()` 智能选择后端(COS 原生 / S3 兼容等),对上层暴露统一接口。
15
15
 
16
16
  ```python
17
17
  from aury.boot.infrastructure.storage import (
18
- StorageManager, StorageConfig, StorageBackend, StorageFile,
18
+ StorageManager,
19
+ StorageConfig,
20
+ StorageBackend,
21
+ StorageFile,
19
22
  )
20
23
 
21
24
  # 默认实例
22
25
  storage = StorageManager.get_instance()
23
26
  await storage.initialize(StorageConfig(
24
- backend=StorageBackend.COS,
27
+ backend=StorageBackend.COS, # 自动选择 COS 原生 SDK 或 S3 兼容模式
25
28
  bucket_name="my-bucket-1250000000",
26
29
  region="ap-guangzhou",
27
30
  endpoint="https://cos.ap-guangzhou.myqcloud.com",
@@ -45,6 +48,12 @@ url = await storage.upload_file(
45
48
  )
46
49
  )
47
50
 
51
+ # 批量上传
52
+ urls = await storage.upload_files([
53
+ StorageFile(object_name="img/1.jpg", data=b"..."),
54
+ StorageFile(object_name="img/2.jpg", data=b"..."),
55
+ ])
56
+
48
57
  # 下载文件
49
58
  content = await storage.download_file("user/123/avatar.png")
50
59
 
@@ -58,7 +67,38 @@ exists = await storage.file_exists("user/123/avatar.png")
58
67
  await storage.delete_file("user/123/avatar.png")
59
68
  ```
60
69
 
61
- ## 10.3 STS 临时凭证(前端直传)
70
+ ## 10.3 高级用法:直接使用 SDKStorageFactory / StorageType
71
+
72
+ 对于需要更精细控制后端类型(如在脚手架或基础设施层扩展存储实现)的场景,可以直接使用 SDK 导出的类型:
73
+
74
+ ```python
75
+ from aury.boot.infrastructure.storage import (
76
+ COSStorage,
77
+ LocalStorage,
78
+ S3Storage,
79
+ SDKStorageFactory, # SDK 工厂(基于 StorageType 枚举)
80
+ StorageConfig,
81
+ StorageFile,
82
+ StorageType,
83
+ )
84
+
85
+ # 使用 StorageType 创建后端
86
+ config = StorageConfig(
87
+ backend=StorageType.COS,
88
+ bucket_name="my-bucket-1250000000",
89
+ region="ap-guangzhou",
90
+ )
91
+
92
+ backend = SDKStorageFactory.from_config(config)
93
+ result = await backend.upload_file(
94
+ StorageFile(object_name="dev/test.txt", data=b"hello"),
95
+ )
96
+ print(result.url)
97
+ ```
98
+
99
+ > 一般业务代码直接通过 `StorageManager` 即可,只有在需要自定义装配流程或编写基础设施扩展时才需要直接使用 `SDKStorageFactory` / `StorageType` / `COSStorage` 等类型。
100
+
101
+ ## 10.4 STS 临时凭证(前端直传)
62
102
 
63
103
  ```python
64
104
  from aury.sdk.storage.sts import (
@@ -95,11 +135,14 @@ return {{
95
135
  }}
96
136
  ```
97
137
 
98
- ## 10.4 本地存储(开发测试)
138
+ ## 10.5 本地存储(开发测试)
99
139
 
100
140
  ```python
101
141
  from aury.boot.infrastructure.storage import (
102
- StorageManager, StorageConfig, StorageBackend, StorageFile,
142
+ StorageManager,
143
+ StorageConfig,
144
+ StorageBackend,
145
+ StorageFile,
103
146
  )
104
147
 
105
148
  storage = StorageManager.get_instance()
@@ -33,7 +33,7 @@ class PaymentAdapter(BaseAdapter):
33
33
  # 真实调用第三方 API
34
34
  response = await self.http_client.post(
35
35
  "https://api.payment.com/orders",
36
- json={"amount": amount, "order_id": order_id},
36
+ json={{"amount": amount, "order_id": order_id}},
37
37
  )
38
38
  return response.json()
39
39
 
@@ -41,12 +41,12 @@ class PaymentAdapter(BaseAdapter):
41
41
  async def create_order_mock(self, amount: int, order_id: str) -> dict:
42
42
  """创建支付订单(Mock 实现)。"""
43
43
  if amount > 100000:
44
- return {"success": False, "error": "金额超限"}
45
- return {
44
+ return {{"success": False, "error": "金额超限"}}
45
+ return {{
46
46
  "success": True,
47
- "transaction_id": f"mock_tx_{order_id}",
47
+ "transaction_id": f"mock_tx_{{order_id}}",
48
48
  "amount": amount,
49
- }
49
+ }}
50
50
 
51
51
  @adapter_method("query_order")
52
52
  async def query_order(self, transaction_id: str) -> dict:
@@ -59,11 +59,11 @@ class PaymentAdapter(BaseAdapter):
59
59
  @query_order.mock
60
60
  async def query_order_mock(self, transaction_id: str) -> dict:
61
61
  """查询支付订单(Mock)。"""
62
- return {
62
+ return {{
63
63
  "transaction_id": transaction_id,
64
64
  "status": "paid",
65
65
  "mock": True,
66
- }
66
+ }}
67
67
  ```
68
68
 
69
69
  ## 16.3 Adapter 配置
@@ -78,7 +78,7 @@ THIRD_PARTY__GATEWAY_MODE=mock
78
78
 
79
79
  # 方法级模式覆盖(JSON 格式)
80
80
  # 例如:query 方法使用 real,其他方法使用全局配置
81
- THIRD_PARTY__METHOD_MODES={"query_order": "real"}
81
+ THIRD_PARTY__METHOD_MODES={{"query_order": "real"}}
82
82
 
83
83
  # Mock 策略:decorator(装饰器)/ auto(自动生成)
84
84
  THIRD_PARTY__MOCK_STRATEGY=decorator
@@ -140,7 +140,7 @@ class WechatAdapter(HttpAdapter):
140
140
  return await self._request(
141
141
  "POST",
142
142
  "/cgi-bin/message/send",
143
- json={"touser": openid, "content": content},
143
+ json={{"touser": openid, "content": content}},
144
144
  )
145
145
 
146
146
  @send_message.mock
@@ -156,13 +156,13 @@ class WechatAdapter(HttpAdapter):
156
156
  ```python
157
157
  settings = AdapterSettings(
158
158
  mode="mock", # 默认 Mock
159
- method_modes={
159
+ method_modes={{
160
160
  "query_order": "real", # 查询走真实接口
161
161
  "create_order": "mock", # 创建走 Mock
162
162
  "refund": "disabled", # 退款禁用
163
- },
163
+ }},
164
+ debug=True,
164
165
  )
165
-
166
166
  adapter = PaymentAdapter("payment", settings)
167
167
 
168
168
  # query_order 会调用真实 API
@@ -237,19 +237,19 @@ class PaymentAdapter(HttpAdapter):
237
237
  self, method: str, args: tuple, kwargs: dict
238
238
  ) -> None:
239
239
  """调用前钩子。"""
240
- logger.info(f"调用 {method},参数: {args}")
240
+ logger.info(f"调用 {{method}},参数: {{args}}")
241
241
 
242
242
  async def _on_after_call(
243
243
  self, method: str, args: tuple, kwargs: dict, result: Any
244
244
  ) -> None:
245
245
  """调用后钩子。"""
246
- logger.info(f"{method} 返回: {result}")
246
+ logger.info(f"{{method}} 返回: {{result}}")
247
247
 
248
248
  async def _on_call_error(
249
249
  self, method: str, args: tuple, kwargs: dict, error: Exception
250
250
  ) -> None:
251
251
  """调用异常钩子。"""
252
- logger.error(f"{method} 异常: {error}")
252
+ logger.error(f"{{method}} 异常: {{error}}")
253
253
  # 可以在这里发送告警
254
254
  ```
255
255
 
@@ -277,11 +277,11 @@ class CompositePaymentAdapter(BaseAdapter):
277
277
  @pay.mock
278
278
  async def pay_mock(self, channel: str, amount: int, order_id: str) -> dict:
279
279
  """统一 Mock 实现。"""
280
- return {
280
+ return {{
281
281
  "success": True,
282
282
  "channel": channel,
283
- "transaction_id": f"mock_{channel}_{order_id}",
284
- }
283
+ "transaction_id": f"mock_{{channel}}_{{order_id}}",
284
+ }}
285
285
  ```
286
286
 
287
287
  ## 16.8 异常处理
@@ -303,9 +303,9 @@ except AdapterTimeoutError:
303
303
  logger.error("支付适配器超时")
304
304
  # 重试或告警
305
305
  except AdapterValidationError as e:
306
- logger.error(f"参数校验失败: {e}")
306
+ logger.error(f"参数校验失败: {{e}}")
307
307
  except AdapterError as e:
308
- logger.error(f"适配器错误: {e}")
308
+ logger.error(f"适配器错误: {{e}}")
309
309
  ```
310
310
 
311
311
  ## 16.9 最佳实践
@@ -329,16 +329,16 @@ async def create_order_mock(self, amount: int, order_id: str) -> dict:
329
329
  """Mock 应模拟各种场景。"""
330
330
  # 模拟金额校验
331
331
  if amount <= 0:
332
- return {"success": False, "error": "金额必须大于0"}
332
+ return {{"success": False, "error": "金额必须大于0"}}
333
333
  if amount > 100000:
334
- return {"success": False, "error": "金额超限"}
334
+ return {{"success": False, "error": "金额超限"}}
335
335
 
336
336
  # 模拟偶发失败(可选)
337
337
  import random
338
338
  if random.random() < 0.01:
339
- return {"success": False, "error": "系统繁忙"}
339
+ return {{"success": False, "error": "系统繁忙"}}
340
340
 
341
- return {"success": True, "transaction_id": f"mock_{order_id}"}
341
+ return {{"success": True, "transaction_id": f"mock_{{order_id}}"}}
342
342
  ```
343
343
 
344
344
  ### 3. 环境配置建议
@@ -381,11 +381,11 @@ class OrderService(BaseService):
381
381
  async def create_order(self, user_id: str, amount: int) -> Order:
382
382
  """创建订单并发起支付。"""
383
383
  # 1. 创建订单记录
384
- order = await self.order_repo.create({
384
+ order = await self.order_repo.create({{
385
385
  "user_id": user_id,
386
386
  "amount": amount,
387
387
  "status": "pending",
388
- })
388
+ }})
389
389
 
390
390
  # 2. 调用支付适配器
391
391
  pay_result = await self.payment.create_order(amount, str(order.id))
@@ -394,10 +394,10 @@ class OrderService(BaseService):
394
394
  raise PaymentError(pay_result["error"])
395
395
 
396
396
  # 3. 更新订单状态
397
- await self.order_repo.update(order, {
397
+ await self.order_repo.update(order, {{
398
398
  "transaction_id": pay_result["transaction_id"],
399
399
  "status": "paid",
400
- })
400
+ }})
401
401
 
402
402
  return order
403
403
  ```
@@ -59,6 +59,31 @@ aury scheduler # 独立运行调度器
59
59
  aury worker # 运行 Dramatiq Worker
60
60
  ```
61
61
 
62
+ ## 包管理
63
+
64
+ ```bash
65
+ # 查看所有可用模块
66
+ aury pkg list
67
+
68
+ # 查看预设配置
69
+ aury pkg preset
70
+ aury pkg preset api # 查看某个预设详情
71
+
72
+ # 安装模块
73
+ aury pkg install postgres redis # 安装指定模块
74
+ aury pkg install --preset api # 按预设安装(postgres + redis + admin)
75
+ aury pkg install --preset worker # 按预设安装(任务队列 + 调度器)
76
+
77
+ # 卸载模块
78
+ aury pkg remove redis
79
+ ```
80
+
81
+ 可用预设:
82
+ - `minimal` - 本地开发(sqlite)
83
+ - `api` - API 服务(postgres + redis + admin)
84
+ - `worker` - 后台 Worker(postgres + redis + tasks + rabbitmq + scheduler)
85
+ - `full` - 完整功能
86
+
62
87
  ## 环境变量配置
63
88
 
64
89
  所有配置项都可通过环境变量设置,优先级:命令行参数 > 环境变量 > .env 文件 > 默认值
@@ -9,6 +9,8 @@
9
9
  LOG__LEVEL=INFO
10
10
  """
11
11
 
12
+ from functools import lru_cache
13
+
12
14
  from aury.boot.application.config import BaseConfig
13
15
 
14
16
 
@@ -28,3 +30,9 @@ class AppConfig(BaseConfig):
28
30
  # 添加自定义配置项
29
31
  # my_setting: str = Field(default="value", description="自定义配置")
30
32
  pass
33
+
34
+
35
+ @lru_cache
36
+ def get_settings() -> AppConfig:
37
+ """获取应用配置单例。"""
38
+ return AppConfig()
@@ -5,34 +5,45 @@
5
5
 
6
6
  # 从 SDK 直接导出核心类型
7
7
  from aury.sdk.storage.storage import (
8
+ COSStorage, # 可选依赖,未安装 cos extras 时为 None
8
9
  IStorage,
9
10
  LocalStorage,
10
11
  S3Storage, # 可选依赖,未安装 aws extras 时为 None
11
12
  StorageBackend,
12
13
  StorageConfig,
13
14
  StorageFile,
15
+ StorageType,
14
16
  UploadResult,
15
17
  )
16
18
 
19
+ # SDK 工厂(基于 StorageType 枚举)
20
+ from aury.sdk.storage.storage import StorageFactory as SDKStorageFactory
21
+
17
22
  from .base import StorageManager
18
23
  from .exceptions import StorageBackendError, StorageError, StorageNotFoundError
24
+
25
+ # Boot 工厂(注册机制)
19
26
  from .factory import StorageFactory
20
27
 
21
28
  __all__ = [
22
29
  # SDK 类型
30
+ "COSStorage",
23
31
  "IStorage",
24
32
  "LocalStorage",
25
33
  "S3Storage",
26
34
  "StorageBackend",
27
- "StorageBackendError",
28
35
  "StorageConfig",
36
+ "StorageFile",
37
+ "StorageType",
38
+ "UploadResult",
39
+ # 工厂
40
+ "SDKStorageFactory", # SDK 工厂(基于枚举类型)
41
+ "StorageFactory", # Boot 工厂(注册机制)
29
42
  # 异常
43
+ "StorageBackendError",
30
44
  "StorageError",
31
- "StorageFactory",
32
- "StorageFile",
33
- # 管理器与工厂
34
- "StorageManager",
35
45
  "StorageNotFoundError",
36
- "UploadResult",
46
+ # 管理器
47
+ "StorageManager",
37
48
  ]
38
49
 
@@ -9,11 +9,12 @@ from aury.boot.common.logging import logger
9
9
  from aury.sdk.storage.storage import (
10
10
  IStorage,
11
11
  LocalStorage,
12
- S3Storage,
13
- StorageBackend,
14
12
  StorageConfig,
15
13
  StorageFile,
16
14
  )
15
+ from aury.sdk.storage.storage import (
16
+ StorageFactory as SDKStorageFactory,
17
+ )
17
18
 
18
19
 
19
20
  class StorageManager:
@@ -78,11 +79,8 @@ class StorageManager:
78
79
  self: 支持链式调用
79
80
  """
80
81
  self._config = config
81
- if config.backend == StorageBackend.LOCAL:
82
- self._backend = LocalStorage(base_path=config.base_path or "./storage")
83
- else:
84
- # S3/COS/OSS/MinIO 统一走 S3Storage
85
- self._backend = S3Storage(config)
82
+ # 使用 SDK 的 StorageFactory 创建后端实例
83
+ self._backend = SDKStorageFactory.from_config(config)
86
84
  logger.info(f"存储管理器初始化完成: {config.backend.value}")
87
85
  return self
88
86
 
@@ -168,8 +166,7 @@ class StorageManager:
168
166
  __all__ = [
169
167
  "IStorage",
170
168
  "LocalStorage",
171
- "S3Storage",
172
- "StorageBackend",
169
+ "SDKStorageFactory",
173
170
  "StorageConfig",
174
171
  "StorageFile",
175
172
  "StorageManager",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aury-boot
3
- Version: 0.0.10
3
+ Version: 0.0.12
4
4
  Summary: Aury Boot - 基于 FastAPI 生态的企业级 API 开发框架
5
5
  Requires-Python: >=3.13
6
6
  Requires-Dist: alembic>=1.17.2
@@ -1,5 +1,5 @@
1
1
  aury/boot/__init__.py,sha256=pCno-EInnpIBa1OtxNYF-JWf9j95Cd2h6vmu0xqa_-4,1791
2
- aury/boot/_version.py,sha256=qWM7tuFjLDSZffhqImvB3QeSymOYGxPQLtsW7Q2jQD0,706
2
+ aury/boot/_version.py,sha256=AZGfWDETy7COMkzrSothvXJ8vYrPI7kp7_Tcfcp9hUQ,706
3
3
  aury/boot/application/__init__.py,sha256=0o_XmiwFCeAu06VHggS8I1e7_nSMoRq0Hcm0fYfCywU,3071
4
4
  aury/boot/application/adapter/__init__.py,sha256=e1bcSb1bxUMfofTwiCuHBZJk5-STkMCWPF2EJXHQ7UU,3976
5
5
  aury/boot/application/adapter/base.py,sha256=Ar_66fiHPDEmV-1DKnqXKwc53p3pozG31bgTJTEUriY,15763
@@ -32,7 +32,7 @@ aury/boot/application/middleware/__init__.py,sha256=T01fmbcdO0Sm6JE74g23uuDyebBG
32
32
  aury/boot/application/middleware/logging.py,sha256=VXd472GJpOQ_n54896tQHauoPSdXyklGD_ZgMoIKsHQ,12633
33
33
  aury/boot/application/migrations/__init__.py,sha256=Z5Gizx7f3AImRcl3cooiIDAZcNi5W-6GvB7mK5w1TNA,204
34
34
  aury/boot/application/migrations/manager.py,sha256=G7mzkNA3MFjyQmM2UwY0ZFNgGGVS4W5GoG2Sbj5AUXk,23685
35
- aury/boot/application/migrations/setup.py,sha256=P89QSMV2JQQb1FuyA9KHhgqSzKWZneCmOtOrLvEmKYQ,6261
35
+ aury/boot/application/migrations/setup.py,sha256=cxzBIHGzRXhlIYs4_ZW6P85Z9A7uVnTq_dmQFKp3ha4,6485
36
36
  aury/boot/application/rpc/__init__.py,sha256=0mVyksLbTOLYMN4OtYrdf9naBNVQnAU9pt2kS2w_9ZY,2064
37
37
  aury/boot/application/rpc/base.py,sha256=KqdWupF2PTizr--jE0KgJUDCfBap72ZWk9FtU5FM9_8,2618
38
38
  aury/boot/application/rpc/client.py,sha256=ApW4h_DrwnnkAh921TVUd4fvdWP-rVIse3VW1_1TLPk,9113
@@ -42,12 +42,13 @@ aury/boot/application/scheduler/runner.py,sha256=g8CM5bDtjiiaMV3662fAOAzh4_bzlQl
42
42
  aury/boot/application/server/__init__.py,sha256=WgSeWItN6oXUzYr1rflGreFiASkLXVt0Z3qkGGtLTI0,8964
43
43
  aury/boot/commands/__init__.py,sha256=8HqWCNefFDk1I-NyViUfCYjynzV7YsdnIsbgoj_sJjo,1486
44
44
  aury/boot/commands/add.py,sha256=JRJir92oFHwIBtIKKEjQ7trUhfb9-kCH84x_7saV2gI,2647
45
- aury/boot/commands/app.py,sha256=-kHcWdZ_D4xDwjOiUkcNSCTYKIUYz39HJV87eaZpeY8,7557
45
+ aury/boot/commands/app.py,sha256=k0zHzR3ckt17laAYk6WwwS-lqzS-N8811XjKC-7lerg,7857
46
46
  aury/boot/commands/config.py,sha256=gPkG_jSWrXidjpyVdzABH7uRhoCgX5yrOcdKabtX5wY,4928
47
47
  aury/boot/commands/docker.py,sha256=7mKorZCPZgxH1XFslzo6W-uzpe61hGXz86JKOhOeBlo,9006
48
48
  aury/boot/commands/docs.py,sha256=-uCvrKj0_shdeBY08W6zx5vNpsBM3yc_IIOOQzoFbqE,11647
49
49
  aury/boot/commands/generate.py,sha256=WZieSXuofxJOC7NBiVGpBigB9NZ4GMcF2F1ReTNun1I,44420
50
- aury/boot/commands/init.py,sha256=HcfBLaU3MwF0tSRTVUEUEA__INwB_cbF7DKRPQ_1Lf0,32358
50
+ aury/boot/commands/init.py,sha256=4JsQ6U48ZfH_Vzu83k3439gc77Zcmn9arKHgH_kq7HM,32808
51
+ aury/boot/commands/pkg.py,sha256=SyHpeSh0vuGVHNmv97-0ZX7AjuSttfZkOs7T86Pq-NQ,14489
51
52
  aury/boot/commands/scheduler.py,sha256=BCIGQcGryXpsYNF-mncP6v5kNoz6DZ10DMzMKVDiXxA,3516
52
53
  aury/boot/commands/worker.py,sha256=qAcPdoKpMBLYoi45X_y2-nobuYKxscJpooEB_0HhM4o,4163
53
54
  aury/boot/commands/migrate/__init__.py,sha256=W9OhkX8ILdolySofgdP2oYoJGG9loQd5FeSwkniU3qM,455
@@ -63,7 +64,7 @@ aury/boot/commands/templates/generate/service.py.tpl,sha256=2hwQ8e4a5d_bIMx_jGDo
63
64
  aury/boot/commands/templates/project/AGENTS.md.tpl,sha256=lzRh23-8Buw5JgZ_BRuMH15gh6WmxJRU4dbZ57fyntA,7309
64
65
  aury/boot/commands/templates/project/README.md.tpl,sha256=oCeBiukk6Pa3hrCKybkfM2sIRHsPZ15nlwuFTUSFDwY,2459
65
66
  aury/boot/commands/templates/project/admin_console_init.py.tpl,sha256=K81L14thyEhRA8lFCQJVZL_NU22-sBz0xS68MJPeoCo,1541
66
- aury/boot/commands/templates/project/config.py.tpl,sha256=ZCZHHvTOv3dm0VcJqmqkvoIEzKoBBcfne_UYnqimc7s,869
67
+ aury/boot/commands/templates/project/config.py.tpl,sha256=H_B05FypBJxTjb7qIL91zC1C9e37Pk7C9gO0-b3CqNs,1009
67
68
  aury/boot/commands/templates/project/conftest.py.tpl,sha256=chbETK81Hy26cWz6YZ2cFgy7HbnABzYCqeyMzgpa3eI,726
68
69
  aury/boot/commands/templates/project/gitignore.tpl,sha256=OI0nt9u2E9EC-jAMoh3gpqamsWo18uDgyPybgee_snQ,3053
69
70
  aury/boot/commands/templates/project/main.py.tpl,sha256=Q61ve3o1VkNPv8wcQK7lUosne18JWYeItxoXVNNoYJM,1070
@@ -77,14 +78,14 @@ aury/boot/commands/templates/project/aury_docs/06-exception.md.tpl,sha256=Tv_Q5l
77
78
  aury/boot/commands/templates/project/aury_docs/07-cache.md.tpl,sha256=EQMI7vJIwJT-VdG4p1GMCDEo58DCO1n6V-MvUzGSaS0,3411
78
79
  aury/boot/commands/templates/project/aury_docs/08-scheduler.md.tpl,sha256=zk7RHjtx_QGjmeLy04Nk_qSc8sofTrubS2Tg7DxfEl4,858
79
80
  aury/boot/commands/templates/project/aury_docs/09-tasks.md.tpl,sha256=swHOQ_pWPtW8Bsy1arPu2OeIgs1FoKsJ2AsVSYUWPHY,931
80
- aury/boot/commands/templates/project/aury_docs/10-storage.md.tpl,sha256=5zamjmVxp3q16De1w_il2vdzEho1O_FoZBN8PIVp2aI,2976
81
+ aury/boot/commands/templates/project/aury_docs/10-storage.md.tpl,sha256=mhe0j0S51ndPJLjaQ6yD8OPYBEO02NHumJVbBvz2qkw,4320
81
82
  aury/boot/commands/templates/project/aury_docs/11-logging.md.tpl,sha256=jdczsKrQuvS3KbR2KdfCu0fUbFNbvP8DHzfYASV-4N0,3397
82
83
  aury/boot/commands/templates/project/aury_docs/12-admin.md.tpl,sha256=6z3mN54qP2jtpTFOJBLVexvEv0ZHXYKjncvpZG4yOdw,1883
83
84
  aury/boot/commands/templates/project/aury_docs/13-channel.md.tpl,sha256=rdtlog3ajcSsT0fq9a_US3MPcZhTvDo72eT6hetg4aI,3376
84
85
  aury/boot/commands/templates/project/aury_docs/14-mq.md.tpl,sha256=4bxLQBbCi0Fue0VQWOPt6acZ5P00BoLkCoLPQe_8k4U,2396
85
86
  aury/boot/commands/templates/project/aury_docs/15-events.md.tpl,sha256=a4wQRgVPuYUGTGmw_lX1HJH_yFTbD30mBz7Arc4zgfs,3361
86
- aury/boot/commands/templates/project/aury_docs/16-adapter.md.tpl,sha256=wPbDKoIMw-d-wwvSWHGm0OF4r9PtoAsQWwTK0BJESCA,11377
87
- aury/boot/commands/templates/project/aury_docs/99-cli.md.tpl,sha256=s0tIhVEi7N9AOABGsFx0hD1g6YutG5ehAG0XMQQva0E,3020
87
+ aury/boot/commands/templates/project/aury_docs/16-adapter.md.tpl,sha256=mKLbvIJxHNQOIeDLJH_v5EYaZYEu54K4jGSk8kEianA,11444
88
+ aury/boot/commands/templates/project/aury_docs/99-cli.md.tpl,sha256=_dXMK8V7HJAsA_6R1SboD8Rv63s7sDPolvQoSQvKXgA,3667
88
89
  aury/boot/commands/templates/project/env_templates/_header.tpl,sha256=Pt0X_I25o1th3CLR228L2-nlcC-lIkN8cPailohBEkU,513
89
90
  aury/boot/commands/templates/project/env_templates/admin.tpl,sha256=wWt3iybOpBHtuw6CkoUJ1bzEL0aNgOzKDEkMKhI2oag,2032
90
91
  aury/boot/commands/templates/project/env_templates/cache.tpl,sha256=_sK-p_FECj4mVvggNvgb4Wu0yGii0Ocz560syG7DU2c,498
@@ -176,8 +177,8 @@ aury/boot/infrastructure/mq/backends/redis.py,sha256=i8KECToIFEZ6CnHyNCk34_xdff5
176
177
  aury/boot/infrastructure/scheduler/__init__.py,sha256=eTRJ5dSPcKvyFvLVtraoQteXTTDDGwIrmw06J2hoNdA,323
177
178
  aury/boot/infrastructure/scheduler/exceptions.py,sha256=ROltrhSctVWA-6ulnjuYeHAk3ZF-sykDoesuierYzew,634
178
179
  aury/boot/infrastructure/scheduler/manager.py,sha256=vvVditIJC4WaeqWWpUXSmMdkqnlUHREPd0BRPSNpnyY,15554
179
- aury/boot/infrastructure/storage/__init__.py,sha256=sUfZqHFLYeutssN5t_LcnOApnxwOreXy-1YtDPmtEdo,866
180
- aury/boot/infrastructure/storage/base.py,sha256=aWBCmPHF5Hkj8VWH2BGkpdeLfqf9uWECOVTFHNj7knw,5131
180
+ aury/boot/infrastructure/storage/__init__.py,sha256=bA-n3v2S1FX6XsQbLqt0hkgx512MjUn_b8kJo53G6gA,1238
181
+ aury/boot/infrastructure/storage/base.py,sha256=X9aswSMWtKZ6TdG5Rrh6qJpqIVLt1QcFkQAKdyUWPi0,5039
181
182
  aury/boot/infrastructure/storage/exceptions.py,sha256=Av1r94bRkeeeDo6vgAD9e_9YA9Ge6D7F2U1qzUs-8FE,622
182
183
  aury/boot/infrastructure/storage/factory.py,sha256=-ua2N5Uw5K14F1FzkGjlqv9VQCtywuZEB9o_kL465IU,2479
183
184
  aury/boot/infrastructure/tasks/__init__.py,sha256=Ne3VRVj4Y7x51UUkJH8nVrbxucCbalsQB7PmthqSfxY,513
@@ -191,7 +192,7 @@ aury/boot/testing/client.py,sha256=KOg1EemuIVsBG68G5y0DjSxZGcIQVdWQ4ASaHE3o1R0,4
191
192
  aury/boot/testing/factory.py,sha256=8GvwX9qIDu0L65gzJMlrWB0xbmJ-7zPHuwk3eECULcg,5185
192
193
  aury/boot/toolkit/__init__.py,sha256=AcyVb9fDf3CaEmJPNkWC4iGv32qCPyk4BuFKSuNiJRQ,334
193
194
  aury/boot/toolkit/http/__init__.py,sha256=zIPmpIZ9Qbqe25VmEr7jixoY2fkRbLm7NkCB9vKpg6I,11039
194
- aury_boot-0.0.10.dist-info/METADATA,sha256=i5glHELenzosPJRyoJBQcn9ujPccCjMvo9BmnNAqX6E,7981
195
- aury_boot-0.0.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
196
- aury_boot-0.0.10.dist-info/entry_points.txt,sha256=f9KXEkDIGc0BGkgBvsNx_HMz9VhDjNxu26q00jUpDwQ,49
197
- aury_boot-0.0.10.dist-info/RECORD,,
195
+ aury_boot-0.0.12.dist-info/METADATA,sha256=YB6RyOOKJdDaZbfk-RhHL041oPN8DZ0kZFRxlrR8mys,7981
196
+ aury_boot-0.0.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
197
+ aury_boot-0.0.12.dist-info/entry_points.txt,sha256=f9KXEkDIGc0BGkgBvsNx_HMz9VhDjNxu26q00jUpDwQ,49
198
+ aury_boot-0.0.12.dist-info/RECORD,,