synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__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.

Potentially problematic release.


This version of synapse-sdk might be problematic. Click here for more details.

Files changed (261) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +9 -8
  3. synapse_sdk/cli/agent/__init__.py +25 -0
  4. synapse_sdk/cli/agent/config.py +104 -0
  5. synapse_sdk/cli/agent/select.py +197 -0
  6. synapse_sdk/cli/auth.py +104 -0
  7. synapse_sdk/cli/main.py +1025 -0
  8. synapse_sdk/cli/plugin/__init__.py +58 -0
  9. synapse_sdk/cli/plugin/create.py +566 -0
  10. synapse_sdk/cli/plugin/job.py +196 -0
  11. synapse_sdk/cli/plugin/publish.py +322 -0
  12. synapse_sdk/cli/plugin/run.py +131 -0
  13. synapse_sdk/cli/plugin/test.py +200 -0
  14. synapse_sdk/clients/README.md +239 -0
  15. synapse_sdk/clients/__init__.py +5 -0
  16. synapse_sdk/clients/_template.py +266 -0
  17. synapse_sdk/clients/agent/__init__.py +84 -29
  18. synapse_sdk/clients/agent/async_ray.py +289 -0
  19. synapse_sdk/clients/agent/container.py +83 -0
  20. synapse_sdk/clients/agent/plugin.py +101 -0
  21. synapse_sdk/clients/agent/ray.py +296 -39
  22. synapse_sdk/clients/backend/__init__.py +152 -12
  23. synapse_sdk/clients/backend/annotation.py +164 -22
  24. synapse_sdk/clients/backend/core.py +101 -0
  25. synapse_sdk/clients/backend/data_collection.py +292 -0
  26. synapse_sdk/clients/backend/hitl.py +87 -0
  27. synapse_sdk/clients/backend/integration.py +374 -46
  28. synapse_sdk/clients/backend/ml.py +134 -22
  29. synapse_sdk/clients/backend/models.py +247 -0
  30. synapse_sdk/clients/base.py +538 -59
  31. synapse_sdk/clients/exceptions.py +35 -7
  32. synapse_sdk/clients/pipeline/__init__.py +5 -0
  33. synapse_sdk/clients/pipeline/client.py +636 -0
  34. synapse_sdk/clients/protocols.py +178 -0
  35. synapse_sdk/clients/utils.py +86 -8
  36. synapse_sdk/clients/validation.py +58 -0
  37. synapse_sdk/enums.py +76 -0
  38. synapse_sdk/exceptions.py +168 -0
  39. synapse_sdk/integrations/__init__.py +74 -0
  40. synapse_sdk/integrations/_base.py +119 -0
  41. synapse_sdk/integrations/_context.py +53 -0
  42. synapse_sdk/integrations/ultralytics/__init__.py +78 -0
  43. synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
  44. synapse_sdk/integrations/ultralytics/_patches.py +124 -0
  45. synapse_sdk/loggers.py +476 -95
  46. synapse_sdk/mcp/MCP.md +69 -0
  47. synapse_sdk/mcp/__init__.py +48 -0
  48. synapse_sdk/mcp/__main__.py +6 -0
  49. synapse_sdk/mcp/config.py +349 -0
  50. synapse_sdk/mcp/prompts/__init__.py +4 -0
  51. synapse_sdk/mcp/resources/__init__.py +4 -0
  52. synapse_sdk/mcp/server.py +1352 -0
  53. synapse_sdk/mcp/tools/__init__.py +6 -0
  54. synapse_sdk/plugins/__init__.py +133 -9
  55. synapse_sdk/plugins/action.py +229 -0
  56. synapse_sdk/plugins/actions/__init__.py +82 -0
  57. synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
  58. synapse_sdk/plugins/actions/dataset/action.py +471 -0
  59. synapse_sdk/plugins/actions/export/__init__.py +55 -0
  60. synapse_sdk/plugins/actions/export/action.py +183 -0
  61. synapse_sdk/plugins/actions/export/context.py +59 -0
  62. synapse_sdk/plugins/actions/inference/__init__.py +84 -0
  63. synapse_sdk/plugins/actions/inference/action.py +285 -0
  64. synapse_sdk/plugins/actions/inference/context.py +81 -0
  65. synapse_sdk/plugins/actions/inference/deployment.py +322 -0
  66. synapse_sdk/plugins/actions/inference/serve.py +252 -0
  67. synapse_sdk/plugins/actions/train/__init__.py +54 -0
  68. synapse_sdk/plugins/actions/train/action.py +326 -0
  69. synapse_sdk/plugins/actions/train/context.py +57 -0
  70. synapse_sdk/plugins/actions/upload/__init__.py +49 -0
  71. synapse_sdk/plugins/actions/upload/action.py +165 -0
  72. synapse_sdk/plugins/actions/upload/context.py +61 -0
  73. synapse_sdk/plugins/config.py +98 -0
  74. synapse_sdk/plugins/context/__init__.py +109 -0
  75. synapse_sdk/plugins/context/env.py +113 -0
  76. synapse_sdk/plugins/datasets/__init__.py +113 -0
  77. synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
  78. synapse_sdk/plugins/datasets/converters/base.py +347 -0
  79. synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
  80. synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
  81. synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
  82. synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
  83. synapse_sdk/plugins/datasets/formats/dm.py +351 -0
  84. synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
  85. synapse_sdk/plugins/decorators.py +83 -0
  86. synapse_sdk/plugins/discovery.py +790 -0
  87. synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
  88. synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
  89. synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
  90. synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
  91. synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
  92. synapse_sdk/plugins/docs/README.md +513 -0
  93. synapse_sdk/plugins/docs/STEP.md +656 -0
  94. synapse_sdk/plugins/enums.py +70 -10
  95. synapse_sdk/plugins/errors.py +92 -0
  96. synapse_sdk/plugins/executors/__init__.py +43 -0
  97. synapse_sdk/plugins/executors/local.py +99 -0
  98. synapse_sdk/plugins/executors/ray/__init__.py +18 -0
  99. synapse_sdk/plugins/executors/ray/base.py +282 -0
  100. synapse_sdk/plugins/executors/ray/job.py +298 -0
  101. synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
  102. synapse_sdk/plugins/executors/ray/packaging.py +137 -0
  103. synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
  104. synapse_sdk/plugins/executors/ray/task.py +257 -0
  105. synapse_sdk/plugins/models/__init__.py +26 -0
  106. synapse_sdk/plugins/models/logger.py +173 -0
  107. synapse_sdk/plugins/models/pipeline.py +25 -0
  108. synapse_sdk/plugins/pipelines/__init__.py +81 -0
  109. synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
  110. synapse_sdk/plugins/pipelines/context.py +107 -0
  111. synapse_sdk/plugins/pipelines/display.py +311 -0
  112. synapse_sdk/plugins/runner.py +114 -0
  113. synapse_sdk/plugins/schemas/__init__.py +19 -0
  114. synapse_sdk/plugins/schemas/results.py +152 -0
  115. synapse_sdk/plugins/steps/__init__.py +63 -0
  116. synapse_sdk/plugins/steps/base.py +128 -0
  117. synapse_sdk/plugins/steps/context.py +90 -0
  118. synapse_sdk/plugins/steps/orchestrator.py +128 -0
  119. synapse_sdk/plugins/steps/registry.py +103 -0
  120. synapse_sdk/plugins/steps/utils/__init__.py +20 -0
  121. synapse_sdk/plugins/steps/utils/logging.py +85 -0
  122. synapse_sdk/plugins/steps/utils/timing.py +71 -0
  123. synapse_sdk/plugins/steps/utils/validation.py +68 -0
  124. synapse_sdk/plugins/templates/__init__.py +50 -0
  125. synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
  126. synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
  127. synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
  128. synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
  129. synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
  130. synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
  131. synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
  132. synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
  133. synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
  134. synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
  135. synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
  136. synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
  137. synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
  138. synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
  139. synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
  140. synapse_sdk/plugins/testing/__init__.py +25 -0
  141. synapse_sdk/plugins/testing/sample_actions.py +98 -0
  142. synapse_sdk/plugins/types.py +206 -0
  143. synapse_sdk/plugins/upload.py +595 -64
  144. synapse_sdk/plugins/utils.py +325 -37
  145. synapse_sdk/shared/__init__.py +25 -0
  146. synapse_sdk/utils/__init__.py +1 -0
  147. synapse_sdk/utils/auth.py +74 -0
  148. synapse_sdk/utils/file/__init__.py +58 -0
  149. synapse_sdk/utils/file/archive.py +449 -0
  150. synapse_sdk/utils/file/checksum.py +167 -0
  151. synapse_sdk/utils/file/download.py +286 -0
  152. synapse_sdk/utils/file/io.py +129 -0
  153. synapse_sdk/utils/file/requirements.py +36 -0
  154. synapse_sdk/utils/network.py +168 -0
  155. synapse_sdk/utils/storage/__init__.py +238 -0
  156. synapse_sdk/utils/storage/config.py +188 -0
  157. synapse_sdk/utils/storage/errors.py +52 -0
  158. synapse_sdk/utils/storage/providers/__init__.py +13 -0
  159. synapse_sdk/utils/storage/providers/base.py +76 -0
  160. synapse_sdk/utils/storage/providers/gcs.py +168 -0
  161. synapse_sdk/utils/storage/providers/http.py +250 -0
  162. synapse_sdk/utils/storage/providers/local.py +126 -0
  163. synapse_sdk/utils/storage/providers/s3.py +177 -0
  164. synapse_sdk/utils/storage/providers/sftp.py +208 -0
  165. synapse_sdk/utils/storage/registry.py +125 -0
  166. synapse_sdk/utils/websocket.py +99 -0
  167. synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
  168. synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
  169. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
  170. synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
  171. locale/en/LC_MESSAGES/messages.mo +0 -0
  172. locale/en/LC_MESSAGES/messages.po +0 -39
  173. locale/ko/LC_MESSAGES/messages.mo +0 -0
  174. locale/ko/LC_MESSAGES/messages.po +0 -34
  175. synapse_sdk/cli/create_plugin.py +0 -10
  176. synapse_sdk/clients/agent/core.py +0 -7
  177. synapse_sdk/clients/agent/service.py +0 -15
  178. synapse_sdk/clients/backend/dataset.py +0 -51
  179. synapse_sdk/clients/ray/__init__.py +0 -6
  180. synapse_sdk/clients/ray/core.py +0 -22
  181. synapse_sdk/clients/ray/serve.py +0 -20
  182. synapse_sdk/i18n.py +0 -35
  183. synapse_sdk/plugins/categories/__init__.py +0 -0
  184. synapse_sdk/plugins/categories/base.py +0 -235
  185. synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
  186. synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
  187. synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
  188. synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
  189. synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
  190. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
  191. synapse_sdk/plugins/categories/decorators.py +0 -13
  192. synapse_sdk/plugins/categories/export/__init__.py +0 -0
  193. synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
  194. synapse_sdk/plugins/categories/export/actions/export.py +0 -10
  195. synapse_sdk/plugins/categories/import/__init__.py +0 -0
  196. synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
  197. synapse_sdk/plugins/categories/import/actions/import.py +0 -10
  198. synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
  199. synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
  200. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
  201. synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
  202. synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
  203. synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
  204. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
  205. synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
  206. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
  207. synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
  208. synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
  209. synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
  210. synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
  211. synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
  212. synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
  213. synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
  214. synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
  215. synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
  216. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
  217. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
  218. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
  219. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
  220. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
  221. synapse_sdk/plugins/categories/registry.py +0 -16
  222. synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
  223. synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
  224. synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
  225. synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
  226. synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
  227. synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
  228. synapse_sdk/plugins/categories/templates.py +0 -32
  229. synapse_sdk/plugins/cli/__init__.py +0 -21
  230. synapse_sdk/plugins/cli/publish.py +0 -37
  231. synapse_sdk/plugins/cli/run.py +0 -67
  232. synapse_sdk/plugins/exceptions.py +0 -22
  233. synapse_sdk/plugins/models.py +0 -121
  234. synapse_sdk/plugins/templates/cookiecutter.json +0 -11
  235. synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
  236. synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
  237. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
  238. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
  239. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
  240. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
  241. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
  242. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
  243. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
  244. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
  245. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
  246. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
  247. synapse_sdk/shared/enums.py +0 -8
  248. synapse_sdk/utils/debug.py +0 -5
  249. synapse_sdk/utils/file.py +0 -87
  250. synapse_sdk/utils/module_loading.py +0 -29
  251. synapse_sdk/utils/pydantic/__init__.py +0 -0
  252. synapse_sdk/utils/pydantic/config.py +0 -4
  253. synapse_sdk/utils/pydantic/errors.py +0 -33
  254. synapse_sdk/utils/pydantic/validators.py +0 -7
  255. synapse_sdk/utils/storage.py +0 -91
  256. synapse_sdk/utils/string.py +0 -11
  257. synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
  258. synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
  259. synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
  260. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
  261. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,238 @@
1
+ """Storage utilities module.
2
+
3
+ This module provides a unified interface for working with different storage
4
+ backends including local filesystem, S3, GCS, SFTP, and HTTP.
5
+
6
+ Example:
7
+ >>> from synapse_sdk.utils.storage import get_storage, get_pathlib
8
+ >>>
9
+ >>> # Local filesystem
10
+ >>> storage = get_storage({
11
+ ... 'provider': 'local',
12
+ ... 'configuration': {'location': '/data'}
13
+ ... })
14
+ >>>
15
+ >>> # S3-compatible storage
16
+ >>> storage = get_storage({
17
+ ... 'provider': 's3',
18
+ ... 'configuration': {
19
+ ... 'bucket_name': 'my-bucket',
20
+ ... 'access_key': 'AKIAIOSFODNN7EXAMPLE',
21
+ ... 'secret_key': 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
22
+ ... }
23
+ ... })
24
+ >>>
25
+ >>> # Get pathlib object for path operations
26
+ >>> path = get_pathlib(storage_config, '/uploads')
27
+ >>> for file in path.rglob('*'):
28
+ ... print(file)
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ from pathlib import Path
34
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
35
+
36
+ if TYPE_CHECKING:
37
+ from upath import UPath
38
+
39
+
40
+ @runtime_checkable
41
+ class StorageProtocol(Protocol):
42
+ """Protocol defining the storage provider interface.
43
+
44
+ All storage providers must implement these methods to be compatible
45
+ with the storage system. Uses structural typing (duck typing) rather
46
+ than inheritance, allowing third-party implementations.
47
+
48
+ Example:
49
+ >>> class CustomStorage:
50
+ ... def upload(self, source: Path, target: str) -> str: ...
51
+ ... def exists(self, target: str) -> bool: ...
52
+ ... def get_url(self, target: str) -> str: ...
53
+ ... def get_pathlib(self, path: str) -> Path: ...
54
+ ... def get_path_file_count(self, pathlib_obj: Path) -> int: ...
55
+ ... def get_path_total_size(self, pathlib_obj: Path) -> int: ...
56
+ >>>
57
+ >>> isinstance(CustomStorage(), StorageProtocol) # True
58
+ """
59
+
60
+ def upload(self, source: Path, target: str) -> str:
61
+ """Upload a file from local source to target path.
62
+
63
+ Args:
64
+ source: Local file path to upload.
65
+ target: Target path in storage (relative to storage root).
66
+
67
+ Returns:
68
+ URL or identifier of the uploaded file.
69
+
70
+ Raises:
71
+ StorageNotFoundError: If source file doesn't exist.
72
+ StorageUploadError: If upload fails.
73
+ """
74
+ ...
75
+
76
+ def exists(self, target: str) -> bool:
77
+ """Check if a file or directory exists at target path.
78
+
79
+ Args:
80
+ target: Path to check (relative to storage root).
81
+
82
+ Returns:
83
+ True if path exists, False otherwise.
84
+ """
85
+ ...
86
+
87
+ def get_url(self, target: str) -> str:
88
+ """Get the URL for accessing a file.
89
+
90
+ Args:
91
+ target: Path to file (relative to storage root).
92
+
93
+ Returns:
94
+ URL string for accessing the file.
95
+ """
96
+ ...
97
+
98
+ def get_pathlib(self, path: str) -> Path | UPath:
99
+ """Get a pathlib-compatible object for the path.
100
+
101
+ Args:
102
+ path: Path relative to storage root.
103
+
104
+ Returns:
105
+ Path object (local) or UPath object (cloud/remote).
106
+ """
107
+ ...
108
+
109
+ def get_path_file_count(self, pathlib_obj: Path | UPath) -> int:
110
+ """Count files recursively in the given path.
111
+
112
+ Args:
113
+ pathlib_obj: Path object from get_pathlib().
114
+
115
+ Returns:
116
+ Number of files (excluding directories).
117
+ """
118
+ ...
119
+
120
+ def get_path_total_size(self, pathlib_obj: Path | UPath) -> int:
121
+ """Calculate total size of files recursively.
122
+
123
+ Args:
124
+ pathlib_obj: Path object from get_pathlib().
125
+
126
+ Returns:
127
+ Total size in bytes.
128
+ """
129
+ ...
130
+
131
+
132
+ def get_storage(connection_param: dict[str, Any]) -> StorageProtocol:
133
+ """Get a storage provider instance from configuration.
134
+
135
+ Args:
136
+ connection_param: Dictionary with 'provider' and 'configuration' keys.
137
+ Example: {'provider': 's3', 'configuration': {'bucket_name': '...'}}
138
+
139
+ Returns:
140
+ Storage provider instance implementing StorageProtocol.
141
+
142
+ Raises:
143
+ StorageConfigError: If configuration is invalid.
144
+ StorageProviderNotFoundError: If provider is not registered.
145
+
146
+ Example:
147
+ >>> config = {
148
+ ... 'provider': 's3',
149
+ ... 'configuration': {
150
+ ... 'bucket_name': 'my-bucket',
151
+ ... 'access_key': 'AKIAIOSFODNN7EXAMPLE',
152
+ ... 'secret_key': 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
153
+ ... }
154
+ ... }
155
+ >>> storage = get_storage(config)
156
+ """
157
+ from synapse_sdk.utils.storage.config import StorageConfig
158
+ from synapse_sdk.utils.storage.registry import get_provider_class
159
+
160
+ # Validate configuration with Pydantic
161
+ storage_config = StorageConfig.model_validate(connection_param)
162
+
163
+ # Get provider class and instantiate
164
+ provider_cls = get_provider_class(storage_config.provider)
165
+ return provider_cls(storage_config.configuration)
166
+
167
+
168
+ def get_pathlib(storage_config: dict[str, Any], path_root: str) -> Path | UPath:
169
+ """Get pathlib object for a path in storage.
170
+
171
+ Convenience function that combines get_storage() and get_pathlib().
172
+
173
+ Args:
174
+ storage_config: Storage configuration dict.
175
+ path_root: Root path to get pathlib for.
176
+
177
+ Returns:
178
+ Path or UPath object.
179
+
180
+ Example:
181
+ >>> config = {'provider': 'local', 'configuration': {'location': '/data'}}
182
+ >>> path = get_pathlib(config, '/uploads')
183
+ >>> path.exists()
184
+ True
185
+ """
186
+ storage = get_storage(storage_config)
187
+ return storage.get_pathlib(path_root)
188
+
189
+
190
+ def get_path_file_count(storage_config: dict[str, Any], path_root: str) -> int:
191
+ """Get file count in a storage path.
192
+
193
+ Args:
194
+ storage_config: Storage configuration dict.
195
+ path_root: Root path to count files in.
196
+
197
+ Returns:
198
+ Number of files.
199
+
200
+ Example:
201
+ >>> config = {'provider': 'local', 'configuration': {'location': '/data'}}
202
+ >>> count = get_path_file_count(config, '/uploads')
203
+ >>> print(f'Found {count} files')
204
+ """
205
+ storage = get_storage(storage_config)
206
+ pathlib_obj = storage.get_pathlib(path_root)
207
+ return storage.get_path_file_count(pathlib_obj)
208
+
209
+
210
+ def get_path_total_size(storage_config: dict[str, Any], path_root: str) -> int:
211
+ """Get total size of files in a storage path.
212
+
213
+ Args:
214
+ storage_config: Storage configuration dict.
215
+ path_root: Root path to calculate size for.
216
+
217
+ Returns:
218
+ Total size in bytes.
219
+
220
+ Example:
221
+ >>> config = {'provider': 'local', 'configuration': {'location': '/data'}}
222
+ >>> size = get_path_total_size(config, '/uploads')
223
+ >>> print(f'Total size: {size / 1024 / 1024:.2f} MB')
224
+ """
225
+ storage = get_storage(storage_config)
226
+ pathlib_obj = storage.get_pathlib(path_root)
227
+ return storage.get_path_total_size(pathlib_obj)
228
+
229
+
230
+ __all__ = [
231
+ # Protocol
232
+ 'StorageProtocol',
233
+ # Public API functions
234
+ 'get_storage',
235
+ 'get_pathlib',
236
+ 'get_path_file_count',
237
+ 'get_path_total_size',
238
+ ]
@@ -0,0 +1,188 @@
1
+ """Storage configuration models using Pydantic v2."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Literal
6
+
7
+ from pydantic import BaseModel, Field, field_validator, model_validator
8
+
9
+
10
+ class LocalStorageConfig(BaseModel):
11
+ """Configuration for local filesystem storage.
12
+
13
+ Attributes:
14
+ location: Base directory path for storage operations.
15
+ """
16
+
17
+ location: str = Field(..., description='Base directory path')
18
+
19
+ @field_validator('location')
20
+ @classmethod
21
+ def validate_location(cls, v: str) -> str:
22
+ if not v:
23
+ raise ValueError('location cannot be empty')
24
+ return v
25
+
26
+
27
+ class S3StorageConfig(BaseModel):
28
+ """Configuration for S3-compatible storage (AWS S3, MinIO).
29
+
30
+ Attributes:
31
+ bucket_name: S3 bucket name.
32
+ access_key: AWS access key ID.
33
+ secret_key: AWS secret access key.
34
+ region_name: AWS region (default: us-east-1).
35
+ endpoint_url: Custom endpoint for S3-compatible services (MinIO).
36
+ """
37
+
38
+ bucket_name: str
39
+ access_key: str
40
+ secret_key: str
41
+ region_name: str = 'us-east-1'
42
+ endpoint_url: str | None = None
43
+
44
+
45
+ class GCSStorageConfig(BaseModel):
46
+ """Configuration for Google Cloud Storage.
47
+
48
+ Attributes:
49
+ bucket_name: GCS bucket name.
50
+ credentials: Path to service account JSON or credentials dict.
51
+ project: GCP project ID (optional, inferred from credentials).
52
+ """
53
+
54
+ bucket_name: str
55
+ credentials: str | dict[str, Any]
56
+ project: str | None = None
57
+
58
+
59
+ class SFTPStorageConfig(BaseModel):
60
+ """Configuration for SFTP storage.
61
+
62
+ Attributes:
63
+ host: SFTP server hostname.
64
+ username: SSH username.
65
+ password: SSH password (for password auth).
66
+ private_key: Path to private key file (for key auth).
67
+ private_key_passphrase: Passphrase for encrypted private key.
68
+ port: SSH port (default: 22).
69
+ root_path: Base path on remote server.
70
+ """
71
+
72
+ host: str
73
+ username: str
74
+ password: str | None = None
75
+ private_key: str | None = None
76
+ private_key_passphrase: str | None = None
77
+ port: int = 22
78
+ root_path: str = '/'
79
+
80
+ @model_validator(mode='after')
81
+ def validate_auth(self) -> SFTPStorageConfig:
82
+ if not self.password and not self.private_key:
83
+ raise ValueError('Either password or private_key must be provided')
84
+ return self
85
+
86
+
87
+ class HTTPStorageConfig(BaseModel):
88
+ """Configuration for HTTP storage.
89
+
90
+ Attributes:
91
+ base_url: Base URL of the HTTP file server.
92
+ timeout: Request timeout in seconds.
93
+ headers: Optional headers to include in requests.
94
+ """
95
+
96
+ base_url: str
97
+ timeout: int = 30
98
+ headers: dict[str, str] = Field(default_factory=dict)
99
+
100
+ @field_validator('base_url')
101
+ @classmethod
102
+ def validate_base_url(cls, v: str) -> str:
103
+ if not v.startswith(('http://', 'https://')):
104
+ raise ValueError('base_url must start with http:// or https://')
105
+ # Ensure trailing slash
106
+ return v if v.endswith('/') else f'{v}/'
107
+
108
+
109
+ # Type alias for all provider configurations
110
+ ProviderConfig = LocalStorageConfig | S3StorageConfig | GCSStorageConfig | SFTPStorageConfig | HTTPStorageConfig
111
+
112
+ # Provider type literals
113
+ ProviderType = Literal[
114
+ 'local',
115
+ 'file_system', # alias for local (backward compat)
116
+ 's3',
117
+ 'amazon_s3', # alias for s3
118
+ 'minio', # alias for s3
119
+ 'gcs',
120
+ 'gs', # alias for gcs
121
+ 'gcp', # alias for gcs
122
+ 'sftp',
123
+ 'http',
124
+ 'https', # alias for http
125
+ ]
126
+
127
+
128
+ class StorageConfig(BaseModel):
129
+ """Top-level storage configuration model.
130
+
131
+ Attributes:
132
+ provider: Storage provider type.
133
+ configuration: Provider-specific configuration.
134
+
135
+ Example:
136
+ >>> config = StorageConfig(
137
+ ... provider='s3',
138
+ ... configuration={
139
+ ... 'bucket_name': 'my-bucket',
140
+ ... 'access_key': 'AKIAIOSFODNN7EXAMPLE',
141
+ ... 'secret_key': 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
142
+ ... }
143
+ ... )
144
+ """
145
+
146
+ provider: ProviderType
147
+ configuration: dict[str, Any]
148
+
149
+ def get_typed_config(self) -> ProviderConfig:
150
+ """Get configuration as the appropriate typed model.
151
+
152
+ Returns:
153
+ Typed configuration model based on provider.
154
+
155
+ Raises:
156
+ ValueError: If provider is unknown.
157
+ """
158
+ config_map: dict[str, type[BaseModel]] = {
159
+ 'local': LocalStorageConfig,
160
+ 'file_system': LocalStorageConfig,
161
+ 's3': S3StorageConfig,
162
+ 'amazon_s3': S3StorageConfig,
163
+ 'minio': S3StorageConfig,
164
+ 'gcs': GCSStorageConfig,
165
+ 'gs': GCSStorageConfig,
166
+ 'gcp': GCSStorageConfig,
167
+ 'sftp': SFTPStorageConfig,
168
+ 'http': HTTPStorageConfig,
169
+ 'https': HTTPStorageConfig,
170
+ }
171
+
172
+ config_cls = config_map.get(self.provider)
173
+ if not config_cls:
174
+ raise ValueError(f'Unknown provider: {self.provider}')
175
+
176
+ return config_cls.model_validate(self.configuration)
177
+
178
+
179
+ __all__ = [
180
+ 'StorageConfig',
181
+ 'LocalStorageConfig',
182
+ 'S3StorageConfig',
183
+ 'GCSStorageConfig',
184
+ 'SFTPStorageConfig',
185
+ 'HTTPStorageConfig',
186
+ 'ProviderConfig',
187
+ 'ProviderType',
188
+ ]
@@ -0,0 +1,52 @@
1
+ """Storage-specific exceptions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+
8
+ class StorageError(Exception):
9
+ """Base exception for storage-related errors."""
10
+
11
+ def __init__(self, message: str, details: Any = None):
12
+ self.message = message
13
+ self.details = details
14
+ super().__init__(message)
15
+
16
+ def __repr__(self) -> str:
17
+ return f'{self.__class__.__name__}(message={self.message!r}, details={self.details!r})'
18
+
19
+
20
+ class StorageConfigError(StorageError):
21
+ """Raised when storage configuration is invalid."""
22
+
23
+
24
+ class StorageProviderNotFoundError(StorageError):
25
+ """Raised when requested provider is not registered."""
26
+
27
+
28
+ class StorageConnectionError(StorageError):
29
+ """Raised when connection to storage fails."""
30
+
31
+
32
+ class StorageUploadError(StorageError):
33
+ """Raised when file upload fails."""
34
+
35
+
36
+ class StorageNotFoundError(StorageError):
37
+ """Raised when requested path does not exist."""
38
+
39
+
40
+ class StoragePermissionError(StorageError):
41
+ """Raised when access to storage is denied."""
42
+
43
+
44
+ __all__ = [
45
+ 'StorageError',
46
+ 'StorageConfigError',
47
+ 'StorageProviderNotFoundError',
48
+ 'StorageConnectionError',
49
+ 'StorageUploadError',
50
+ 'StorageNotFoundError',
51
+ 'StoragePermissionError',
52
+ ]
@@ -0,0 +1,13 @@
1
+ """Storage providers module.
2
+
3
+ This module exports all built-in storage providers. Import specific
4
+ providers directly for better control over dependencies.
5
+
6
+ Note: Providers are imported lazily via registry to avoid
7
+ loading optional dependencies (boto3, gcsfs, paramiko) at import time.
8
+ Direct imports are available for explicit usage.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ __all__: list[str] = []
@@ -0,0 +1,76 @@
1
+ """Shared implementation for storage providers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from upath import UPath
10
+
11
+
12
+ class _BaseStorageMixin:
13
+ """Shared implementation for storage providers.
14
+
15
+ This mixin provides common implementations for file counting
16
+ and size calculation that work with both Path and UPath objects.
17
+ """
18
+
19
+ def _count_files(self, pathlib_obj: Path | UPath) -> int:
20
+ """Count files recursively in the given path.
21
+
22
+ Args:
23
+ pathlib_obj: Path object to count files in.
24
+
25
+ Returns:
26
+ Number of files.
27
+ """
28
+ if not pathlib_obj.exists():
29
+ return 0
30
+
31
+ if pathlib_obj.is_file():
32
+ return 1
33
+
34
+ count = 0
35
+ for item in pathlib_obj.rglob('*'):
36
+ if item.is_file():
37
+ count += 1
38
+ return count
39
+
40
+ def _calculate_total_size(self, pathlib_obj: Path | UPath) -> int:
41
+ """Calculate total size of files recursively.
42
+
43
+ Args:
44
+ pathlib_obj: Path object to calculate size for.
45
+
46
+ Returns:
47
+ Total size in bytes.
48
+ """
49
+ if not pathlib_obj.exists():
50
+ return 0
51
+
52
+ if pathlib_obj.is_file():
53
+ return pathlib_obj.stat().st_size
54
+
55
+ total_size = 0
56
+ for item in pathlib_obj.rglob('*'):
57
+ if item.is_file():
58
+ total_size += item.stat().st_size
59
+ return total_size
60
+
61
+ @staticmethod
62
+ def _normalize_path(path: str) -> str:
63
+ """Normalize path by removing leading slashes.
64
+
65
+ Args:
66
+ path: Path string to normalize.
67
+
68
+ Returns:
69
+ Normalized path without leading slashes.
70
+ """
71
+ if path in ('/', ''):
72
+ return ''
73
+ return path.lstrip('/')
74
+
75
+
76
+ __all__ = ['_BaseStorageMixin']