sqlspec 0.21.1__py3-none-any.whl → 0.23.0__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 sqlspec might be problematic. Click here for more details.

@@ -14,7 +14,6 @@ from mypy_extensions import mypyc_attr
14
14
 
15
15
  from sqlspec.exceptions import ImproperConfigurationError, MissingDependencyError
16
16
  from sqlspec.protocols import ObjectStoreProtocol
17
- from sqlspec.storage.capabilities import StorageCapabilities
18
17
  from sqlspec.typing import FSSPEC_INSTALLED, OBSTORE_INSTALLED
19
18
 
20
19
  __all__ = ("StorageRegistry", "storage_registry")
@@ -22,34 +21,52 @@ __all__ = ("StorageRegistry", "storage_registry")
22
21
  logger = logging.getLogger(__name__)
23
22
 
24
23
 
24
+ def _is_local_uri(uri: str) -> bool:
25
+ """Check if URI represents a local filesystem path."""
26
+ if "://" in uri and not uri.startswith("file://"):
27
+ return False
28
+ windows_drive_min_length = 3
29
+ return (
30
+ Path(uri).exists()
31
+ or Path(uri).is_absolute()
32
+ or uri.startswith(("~", ".", "/"))
33
+ or (len(uri) >= windows_drive_min_length and uri[1:3] == ":\\")
34
+ or "/" in uri
35
+ )
36
+
37
+
25
38
  SCHEME_REGEX: Final = re.compile(r"([a-zA-Z0-9+.-]+)://")
26
- FILE_PROTOCOL: Final[str] = "file"
27
- S3_PROTOCOL: Final[str] = "s3"
28
- GCS_PROTOCOL: Final[str] = "gs"
29
- AZURE_PROTOCOL: Final[str] = "az"
39
+
40
+
30
41
  FSSPEC_ONLY_SCHEMES: Final[frozenset[str]] = frozenset({"http", "https", "ftp", "sftp", "ssh"})
31
42
 
32
43
 
33
44
  @mypyc_attr(allow_interpreted_subclasses=True)
34
45
  class StorageRegistry:
35
- """Storage registry with URI-first access and automatic backend selection.
46
+ """Global storage registry for named backend configurations.
36
47
 
37
- Provides URI-first access pattern with automatic backend selection.
38
- Named aliases support complex configurations.
48
+ Allows registering named storage backends that can be accessed from anywhere
49
+ in your application. Backends are automatically selected based on URI scheme
50
+ unless explicitly overridden.
39
51
 
40
52
  Examples:
41
- backend = registry.get("s3://my-bucket/file.parquet")
42
- backend = registry.get("file:///tmp/data.csv")
43
- backend = registry.get("gs://bucket/data.json")
44
-
45
- registry.register_alias(
46
- "production-s3",
47
- uri="s3://prod-bucket/data",
48
- base_path="sqlspec",
49
- aws_access_key_id="...",
50
- aws_secret_access_key="..."
51
- )
52
- backend = registry.get("production-s3")
53
+ # Direct URI access to storage containers
54
+ backend = registry.get("s3://my-bucket")
55
+ backend = registry.get("file:///tmp/data")
56
+ backend = registry.get("gs://my-gcs-bucket")
57
+
58
+ # Named store pattern for environment-specific backends
59
+ # Development
60
+ registry.register_alias("my_app_store", "file:///tmp/dev_data")
61
+
62
+ # Production
63
+ registry.register_alias("my_app_store", "s3://prod-bucket/data")
64
+
65
+ # Access from anywhere in your app
66
+ store = registry.get("my_app_store") # Works in both environments
67
+
68
+ # Force specific backend when multiple options available
69
+ backend = registry.get("s3://bucket", backend="fsspec") # Force fsspec over obstore
53
70
  """
54
71
 
55
72
  __slots__ = ("_alias_configs", "_aliases", "_cache", "_instances")
@@ -60,44 +77,47 @@ class StorageRegistry:
60
77
  self._instances: dict[Union[str, tuple[str, tuple[tuple[str, Any], ...]]], ObjectStoreProtocol] = {}
61
78
  self._cache: dict[str, tuple[str, type[ObjectStoreProtocol]]] = {}
62
79
 
80
+ def _make_hashable(self, obj: Any) -> Any:
81
+ """Convert nested dict/list structures to hashable tuples."""
82
+ if isinstance(obj, dict):
83
+ return tuple(sorted((k, self._make_hashable(v)) for k, v in obj.items()))
84
+ if isinstance(obj, list):
85
+ return tuple(self._make_hashable(item) for item in obj)
86
+ if isinstance(obj, set):
87
+ return tuple(sorted(self._make_hashable(item) for item in obj))
88
+ return obj
89
+
63
90
  def register_alias(
64
- self,
65
- alias: str,
66
- uri: str,
67
- *,
68
- backend: Optional[type[ObjectStoreProtocol]] = None,
69
- base_path: str = "",
70
- config: Optional[dict[str, Any]] = None,
71
- **kwargs: Any,
91
+ self, alias: str, uri: str, *, backend: Optional[str] = None, base_path: str = "", **kwargs: Any
72
92
  ) -> None:
73
93
  """Register a named alias for a storage configuration.
74
94
 
75
95
  Args:
76
- alias: Unique alias name for the configuration
77
- uri: Storage URI (e.g., "s3://bucket", "file:///path")
78
- backend: Backend class to use (auto-detected from URI if not provided)
96
+ alias: Unique alias name (e.g., "my_app_store", "user_uploads")
97
+ uri: Storage URI (e.g., "s3://bucket", "file:///path", "gs://bucket")
98
+ backend: Force specific backend ("local", "fsspec", "obstore") instead of auto-detection
79
99
  base_path: Base path to prepend to all operations
80
- config: Additional configuration dict
81
100
  **kwargs: Backend-specific configuration options
82
101
  """
83
- if backend is None:
84
- backend = self._determine_backend_class(uri)
102
+ backend_cls = self._get_backend_class(backend) if backend else self._determine_backend_class(uri)
85
103
 
86
- config = config or {}
87
- config.update(kwargs)
88
- backend_config = dict(config)
104
+ backend_config = dict(kwargs)
89
105
  if base_path:
90
106
  backend_config["base_path"] = base_path
91
- self._alias_configs[alias] = (backend, uri, backend_config)
107
+ self._alias_configs[alias] = (backend_cls, uri, backend_config)
108
+
92
109
  test_config = dict(backend_config)
93
110
  test_config["uri"] = uri
94
111
  self._aliases[alias] = test_config
95
112
 
96
- def get(self, uri_or_alias: Union[str, Path], **kwargs: Any) -> ObjectStoreProtocol:
113
+ def get(
114
+ self, uri_or_alias: Union[str, Path], *, backend: Optional[str] = None, **kwargs: Any
115
+ ) -> ObjectStoreProtocol:
97
116
  """Get backend instance using URI-first routing with automatic backend selection.
98
117
 
99
118
  Args:
100
- uri_or_alias: URI to resolve directly OR named alias
119
+ uri_or_alias: URI to resolve directly OR named alias (e.g., "my_app_store")
120
+ backend: Force specific backend ("local", "fsspec", "obstore") instead of auto-selection
101
121
  **kwargs: Additional backend-specific configuration options
102
122
 
103
123
  Returns:
@@ -113,24 +133,20 @@ class StorageRegistry:
113
133
  if isinstance(uri_or_alias, Path):
114
134
  uri_or_alias = f"file://{uri_or_alias.resolve()}"
115
135
 
116
- cache_key = (uri_or_alias, tuple(sorted(kwargs.items()))) if kwargs else uri_or_alias
136
+ cache_key = (uri_or_alias, self._make_hashable(kwargs)) if kwargs else uri_or_alias
117
137
  if cache_key in self._instances:
118
138
  return self._instances[cache_key]
119
139
  scheme = self._get_scheme(uri_or_alias)
120
- if not scheme and (
121
- Path(uri_or_alias).exists()
122
- or Path(uri_or_alias).is_absolute()
123
- or uri_or_alias.startswith(("~", "."))
124
- or ":\\" in uri_or_alias
125
- or "/" in uri_or_alias
126
- ):
140
+ if not scheme and _is_local_uri(uri_or_alias):
127
141
  scheme = "file"
128
142
  uri_or_alias = f"file://{uri_or_alias}"
129
143
 
130
144
  if scheme:
131
- instance = self._resolve_from_uri(uri_or_alias, **kwargs)
145
+ instance = self._resolve_from_uri(uri_or_alias, backend_override=backend, **kwargs)
132
146
  elif uri_or_alias in self._alias_configs:
133
147
  backend_cls, stored_uri, config = self._alias_configs[uri_or_alias]
148
+ if backend:
149
+ backend_cls = self._get_backend_class(backend)
134
150
  instance = backend_cls(stored_uri, **{**config, **kwargs})
135
151
  else:
136
152
  msg = f"Unknown storage alias or invalid URI: '{uri_or_alias}'"
@@ -138,36 +154,66 @@ class StorageRegistry:
138
154
  self._instances[cache_key] = instance
139
155
  return instance
140
156
 
141
- def _resolve_from_uri(self, uri: str, **kwargs: Any) -> ObjectStoreProtocol:
142
- """Resolve backend from URI, trying ObStore first, then FSSpec."""
157
+ def _resolve_from_uri(
158
+ self, uri: str, *, backend_override: Optional[str] = None, **kwargs: Any
159
+ ) -> ObjectStoreProtocol:
160
+ """Resolve backend from URI with optional backend override."""
161
+ if backend_override:
162
+ return self._create_backend(backend_override, uri, **kwargs)
143
163
  scheme = self._get_scheme(uri)
164
+
165
+ # For local files, prefer LocalStore first
166
+ if scheme in {None, "file"}:
167
+ return self._create_backend("local", uri, **kwargs)
168
+
169
+ # Try ObStore first if available and appropriate
144
170
  if scheme not in FSSPEC_ONLY_SCHEMES and OBSTORE_INSTALLED:
145
171
  try:
146
172
  return self._create_backend("obstore", uri, **kwargs)
147
173
  except (ValueError, ImportError, NotImplementedError):
148
174
  pass
175
+
176
+ # Try FSSpec if available
149
177
  if FSSPEC_INSTALLED:
150
178
  try:
151
179
  return self._create_backend("fsspec", uri, **kwargs)
152
180
  except (ValueError, ImportError, NotImplementedError):
153
181
  pass
154
- msg = "obstore"
155
- raise MissingDependencyError(msg, "fsspec")
182
+
183
+ # For cloud schemes without backends, provide helpful error
184
+ msg = f"No backend available for URI scheme '{scheme}'. Install obstore or fsspec for cloud storage support."
185
+ raise MissingDependencyError(msg)
156
186
 
157
187
  def _determine_backend_class(self, uri: str) -> type[ObjectStoreProtocol]:
158
188
  """Determine the backend class for a URI based on availability."""
159
189
  scheme = self._get_scheme(uri)
190
+
191
+ # For local files, always use LocalStore
192
+ if scheme in {None, "file"}:
193
+ return self._get_backend_class("local")
194
+
195
+ # FSSpec-only schemes require FSSpec
160
196
  if scheme in FSSPEC_ONLY_SCHEMES and FSSPEC_INSTALLED:
161
197
  return self._get_backend_class("fsspec")
198
+
199
+ # Prefer ObStore for cloud storage if available
162
200
  if OBSTORE_INSTALLED:
163
201
  return self._get_backend_class("obstore")
202
+
203
+ # Fall back to FSSpec if available
164
204
  if FSSPEC_INSTALLED:
165
205
  return self._get_backend_class("fsspec")
166
- msg = f"No backend available for URI scheme '{scheme}'. Install obstore or fsspec."
206
+
207
+ # For cloud schemes without backends, provide helpful error
208
+ msg = f"No backend available for URI scheme '{scheme}'. Install obstore or fsspec for cloud storage support."
167
209
  raise MissingDependencyError(msg)
168
210
 
169
211
  def _get_backend_class(self, backend_type: str) -> type[ObjectStoreProtocol]:
170
212
  """Get backend class by type name."""
213
+ if backend_type == "local":
214
+ from sqlspec.storage.backends.local import LocalStore
215
+
216
+ return cast("type[ObjectStoreProtocol]", LocalStore)
171
217
  if backend_type == "obstore":
172
218
  from sqlspec.storage.backends.obstore import ObStoreBackend
173
219
 
@@ -176,7 +222,7 @@ class StorageRegistry:
176
222
  from sqlspec.storage.backends.fsspec import FSSpecBackend
177
223
 
178
224
  return cast("type[ObjectStoreProtocol]", FSSpecBackend)
179
- msg = f"Unknown backend type: {backend_type}. Supported types: 'obstore', 'fsspec'"
225
+ msg = f"Unknown backend type: {backend_type}. Supported types: 'local', 'obstore', 'fsspec'"
180
226
  raise ValueError(msg)
181
227
 
182
228
  def _create_backend(self, backend_type: str, uri: str, **kwargs: Any) -> ObjectStoreProtocol:
@@ -220,20 +266,5 @@ class StorageRegistry:
220
266
  self._alias_configs.clear()
221
267
  self._aliases.clear()
222
268
 
223
- def get_backend_capabilities(self, uri_or_alias: Union[str, Path]) -> "StorageCapabilities":
224
- """Get capabilities for a backend without creating an instance."""
225
- if isinstance(uri_or_alias, Path):
226
- uri_or_alias = f"file://{uri_or_alias.resolve()}"
227
- if "://" in uri_or_alias:
228
- backend_cls = self._determine_backend_class(uri_or_alias)
229
- elif uri_or_alias in self._alias_configs:
230
- backend_cls, _, _ = self._alias_configs[uri_or_alias]
231
- else:
232
- msg = f"Unknown storage alias or invalid URI: '{uri_or_alias}'"
233
- raise ImproperConfigurationError(msg)
234
- if hasattr(backend_cls, "capabilities"):
235
- return backend_cls.capabilities
236
- return StorageCapabilities()
237
-
238
269
 
239
270
  storage_registry = StorageRegistry()
@@ -29,6 +29,13 @@ ParamSpecT = ParamSpec("ParamSpecT")
29
29
  T = TypeVar("T")
30
30
 
31
31
 
32
+ class NoValue:
33
+ """Sentinel class for missing values."""
34
+
35
+
36
+ NO_VALUE = NoValue()
37
+
38
+
32
39
  class CapacityLimiter:
33
40
  """Limits the number of concurrent operations using a semaphore."""
34
41
 
@@ -240,11 +247,7 @@ def with_ensure_async_(
240
247
  return obj
241
248
 
242
249
 
243
- class NoValue:
244
- """Sentinel class for missing values."""
245
-
246
-
247
- async def get_next(iterable: Any, default: Any = NoValue, *args: Any) -> Any: # pragma: no cover
250
+ async def get_next(iterable: Any, default: Any = NO_VALUE, *args: Any) -> Any: # pragma: no cover
248
251
  """Return the next item from an async iterator.
249
252
 
250
253
  Args:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlspec
3
- Version: 0.21.1
3
+ Version: 0.23.0
4
4
  Summary: SQL Experiments in Python
5
5
  Project-URL: Discord, https://discord.gg/litestar
6
6
  Project-URL: Issue, https://github.com/litestar-org/sqlspec/issues/
@@ -2,14 +2,14 @@ sqlspec/__init__.py,sha256=JkL9cp1h19tz1rCl-3WAep6kbZs8JLxNlb1SrQl8PWc,2114
2
2
  sqlspec/__main__.py,sha256=lXBKZMOXA1uY735Rnsb-GS7aXy0nt22tYmd2X9FcxrY,253
3
3
  sqlspec/__metadata__.py,sha256=IUw6MCTy1oeUJ1jAVYbuJLkOWbiAWorZ5W-E-SAD9N4,395
4
4
  sqlspec/_serialization.py,sha256=6U5-smk2h2yl0i6am2prtOLJTdu4NJQdcLlSfSUMaUQ,2590
5
- sqlspec/_sql.py,sha256=j9WljOgCme4jTfL6NegEWOhK-Rr3JEmhtbneh8ZN1bQ,45221
5
+ sqlspec/_sql.py,sha256=QHx_awVF6yFxcNLcuAk19AnJe1FvX1FwqjxhJcUjXD4,46221
6
6
  sqlspec/_typing.py,sha256=jv-7QHGLrJLfnP86bR-Xcmj3PDoddNZEKDz_vYRBiAU,22684
7
- sqlspec/base.py,sha256=p3qX3nq1qPuLz6AEVizbZ4xpWhMoDXNc7zkPK9ecJac,25467
7
+ sqlspec/base.py,sha256=koDh1AecwCAkntSqSda6J_cpMOLonXiV6hh3GCCXf_s,25459
8
8
  sqlspec/cli.py,sha256=Fe5Wbnrb_fkE9qm4gbBEXx3d0Q7VR-S-1t76ouAx2mg,20120
9
9
  sqlspec/config.py,sha256=PQKKLXst_uMvqvTSQib6qMZfJd-g3Kqqlp7XLn9kA8A,21640
10
10
  sqlspec/exceptions.py,sha256=zBnzQOfYAgqX04GoaC9Io6ardzinldkEuZ3YtR5vr9U,6071
11
- sqlspec/loader.py,sha256=R_lcI8Jg3Jh9jUDFvQyKbBzx20vKH50BKYt8gvTKq7c,23400
12
- sqlspec/protocols.py,sha256=Of6uJyxvawExCEyR3u7jbxOckUcwG0HHOEXmfHyev40,13106
11
+ sqlspec/loader.py,sha256=4Gl4LcdVwEB6-4F-KiCnlI3nzyG3LVOFvRMAQNC64qE,23748
12
+ sqlspec/protocols.py,sha256=jSO2OeZvywqkaIvLRFDkQajJvDlErCICipToUH3Mvoo,12996
13
13
  sqlspec/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  sqlspec/typing.py,sha256=yj8D8O-pkfUVZDfVHEgQaB95-5alwgQbp_sqNJOVhvQ,6301
15
15
  sqlspec/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -72,7 +72,7 @@ sqlspec/builder/mixins/__init__.py,sha256=YXhAzKmQbQtne5j26SKWY8PUxwosl0RhlhLoah
72
72
  sqlspec/builder/mixins/_cte_and_set_ops.py,sha256=gANbAnL7ulkIMS1NG47nd_5laEsjwmEaROu-kTVbbF4,9152
73
73
  sqlspec/builder/mixins/_delete_operations.py,sha256=LCzXoNtPUVgJ5KxhKoQj-METq51g5aDqoEmDmB66e08,1271
74
74
  sqlspec/builder/mixins/_insert_operations.py,sha256=ipU1Hhue6MRFfbouDDXyONGC9-jmW7LYuo4HuMtioJs,10437
75
- sqlspec/builder/mixins/_join_operations.py,sha256=TjVK6uJjhzbg4kmbMBCQkuW-UgTKqpFHvaKcSFOeB_w,11530
75
+ sqlspec/builder/mixins/_join_operations.py,sha256=1CN1E6TwtFng4ftKmIYN0MAJNZLkWISGB2UY1E1tivw,16036
76
76
  sqlspec/builder/mixins/_merge_operations.py,sha256=v2JZbyOZyT5mS0cJo82ckjE9eR2LqoAS-YO2NJWK2a4,24297
77
77
  sqlspec/builder/mixins/_order_limit_operations.py,sha256=i5QXZ_1aCzAhv3VG4Tp08kW8vYqEHU04TRoZPZgS2KA,5573
78
78
  sqlspec/builder/mixins/_pivot_operations.py,sha256=s3b2zSOL0zEXEGXo1lBm5MuqXWl15qMww_krsmFqj3I,6163
@@ -112,13 +112,13 @@ sqlspec/migrations/loaders.py,sha256=wildbpkyHrE--HXspChPOajSHSBUrfG0e6xQ2buze_4
112
112
  sqlspec/migrations/runner.py,sha256=y6fyZi02n8MseKR8XFWXUEOOYQNG_w_DikHVxH9p20M,10730
113
113
  sqlspec/migrations/tracker.py,sha256=hfrZGz8M70SfFniw4aXVtHNg4p8EPFm67vthjfUMUys,6843
114
114
  sqlspec/migrations/utils.py,sha256=Ft5mS1GFiRPLhfUTfPU4ZnEgOkXDnmEHjeeWwfZDcv4,3737
115
- sqlspec/storage/__init__.py,sha256=xWSsq5QXrY7wCsjQYPldfdlm8UEJ-kojU-tWsoldSy0,645
116
- sqlspec/storage/capabilities.py,sha256=vyousxls9jISsykgoybpNHlGWN6Hq_pKcsZ5DmKGWvU,3045
117
- sqlspec/storage/registry.py,sha256=4duy0uOupl0X5VNhwUeQMMWPsraKWyIdjDrKFsFcVG0,9424
118
- sqlspec/storage/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
+ sqlspec/storage/__init__.py,sha256=IX7xLuymGdQKOBQL1sNEA-bzZRJGjncUbQbs6ZToDDE,395
116
+ sqlspec/storage/registry.py,sha256=yJZrLDu1vr86C6En3MlzCPOjUNYkvA5Ow-AKrLEDvpI,10754
117
+ sqlspec/storage/backends/__init__.py,sha256=3oSqwEQ_trU2QoxtUTX-5IFeOvo7WGcESY6gIfcmSaE,24
119
118
  sqlspec/storage/backends/base.py,sha256=KS2JRZILoH_R_xsfKtYkqQ5a1r5OOBDSE5KbibTmhGY,5730
120
- sqlspec/storage/backends/fsspec.py,sha256=8AX8ULwlApMd6WtHDVJBomdvk5UjPtfMf78TJ86gTC8,15994
121
- sqlspec/storage/backends/obstore.py,sha256=v9moaiSVKbpl9MOrb9AAhhSXfQb1ifammrT8WeV1xcw,19735
119
+ sqlspec/storage/backends/fsspec.py,sha256=5X3QhoaAddzMqkeTHs8M5RINj3mrHcnaIVWb8VhC4lQ,13582
120
+ sqlspec/storage/backends/local.py,sha256=vsFnttNesNYjsY_l-KoH6rld51L3E6mxeODtMJawFPQ,12793
121
+ sqlspec/storage/backends/obstore.py,sha256=4RUZAMCF16h1IQ4dEib-0j8TLnhPf0Jhy2lmRJSe7Kw,20512
122
122
  sqlspec/utils/__init__.py,sha256=cNFX26-bLyZTyTfujUitfDkUy1CeG_d-EIr8kZ0z4W8,474
123
123
  sqlspec/utils/correlation.py,sha256=2jvkAY3nkU3UxNU_9pbBR6cz3A1Q1cGG9IaWSSOIb1Q,4195
124
124
  sqlspec/utils/data_transformation.py,sha256=U37zyxR4f5PxsxKdC7QzcMyJxfqpsXUxgH_ch5l3PbY,3951
@@ -128,12 +128,12 @@ sqlspec/utils/logging.py,sha256=zAM7rHJ-KsmAj1yjvU9QFoiwf4Q2hKTere2J62FlllI,3664
128
128
  sqlspec/utils/module_loader.py,sha256=rO4ht-fUSJ3Us7L_7fb_G9bdMCoUSABGUA0pc3ouh9Y,2995
129
129
  sqlspec/utils/serializers.py,sha256=GXsTkJbWAhRS7xDMk6WBouZwPeG4sI_brLdMBlIetNg,318
130
130
  sqlspec/utils/singleton.py,sha256=-j-s6LS0pP_wTEUYIyK2wSdoeIE_tn7O7B-j7_aODRQ,1252
131
- sqlspec/utils/sync_tools.py,sha256=ksfxsvFb1hLrDlxzwdW44OvYgRB0Fr5JDqxswfHwoOs,8744
131
+ sqlspec/utils/sync_tools.py,sha256=ONdhmx1Dq0_c6ReRaTlXzz6dVmAwz6CybCvsTUAVu1g,8768
132
132
  sqlspec/utils/text.py,sha256=ZqaXCVuUbdj_110pdTYjmAxfV3ZtR7J6EixuNazQLFY,3333
133
133
  sqlspec/utils/type_guards.py,sha256=ktXwBQLLqOvk1W2wJcmk3bUprrsegs8nAZ879qDe0AU,32880
134
- sqlspec-0.21.1.dist-info/METADATA,sha256=diZWPfeC58LVHYhjp1WTNH1JOPqkrJZLq3ZwdS5nKVU,23548
135
- sqlspec-0.21.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
136
- sqlspec-0.21.1.dist-info/entry_points.txt,sha256=G-ZqY1Nuuw3Iys7nXw23f6ILenk_Lt47VdK2mhJCWHg,53
137
- sqlspec-0.21.1.dist-info/licenses/LICENSE,sha256=MdujfZ6l5HuLz4mElxlu049itenOR3gnhN1_Nd3nVcM,1078
138
- sqlspec-0.21.1.dist-info/licenses/NOTICE,sha256=Lyir8ozXWov7CyYS4huVaOCNrtgL17P-bNV-5daLntQ,1634
139
- sqlspec-0.21.1.dist-info/RECORD,,
134
+ sqlspec-0.23.0.dist-info/METADATA,sha256=bSrPoVRmVNHSvsvNGO7j_Il8xpIlsHqz82B57bjjbRM,23548
135
+ sqlspec-0.23.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
136
+ sqlspec-0.23.0.dist-info/entry_points.txt,sha256=G-ZqY1Nuuw3Iys7nXw23f6ILenk_Lt47VdK2mhJCWHg,53
137
+ sqlspec-0.23.0.dist-info/licenses/LICENSE,sha256=MdujfZ6l5HuLz4mElxlu049itenOR3gnhN1_Nd3nVcM,1078
138
+ sqlspec-0.23.0.dist-info/licenses/NOTICE,sha256=Lyir8ozXWov7CyYS4huVaOCNrtgL17P-bNV-5daLntQ,1634
139
+ sqlspec-0.23.0.dist-info/RECORD,,
@@ -1,102 +0,0 @@
1
- """Storage backend capability system.
2
-
3
- This module provides a centralized way to track and query storage backend capabilities.
4
- """
5
-
6
- from dataclasses import dataclass
7
- from typing import ClassVar
8
-
9
- from mypy_extensions import mypyc_attr
10
-
11
- __all__ = ("HasStorageCapabilities", "StorageCapabilities")
12
-
13
-
14
- @dataclass
15
- class StorageCapabilities:
16
- """Tracks capabilities of a storage backend."""
17
-
18
- supports_read: bool = True
19
- supports_write: bool = True
20
- supports_delete: bool = True
21
- supports_list: bool = True
22
- supports_exists: bool = True
23
- supports_copy: bool = True
24
- supports_move: bool = True
25
- supports_metadata: bool = True
26
-
27
- supports_arrow: bool = False
28
- supports_streaming: bool = False
29
- supports_async: bool = False
30
- supports_batch_operations: bool = False
31
- supports_multipart_upload: bool = False
32
- supports_compression: bool = False
33
-
34
- supports_s3_select: bool = False
35
- supports_gcs_compose: bool = False
36
- supports_azure_snapshots: bool = False
37
-
38
- is_remote: bool = True
39
- is_cloud_native: bool = False
40
- has_low_latency: bool = False
41
-
42
- @classmethod
43
- def local_filesystem(cls) -> "StorageCapabilities":
44
- """Capabilities for local filesystem backend."""
45
- return cls(
46
- is_remote=False, has_low_latency=True, supports_arrow=True, supports_streaming=True, supports_async=True
47
- )
48
-
49
- @classmethod
50
- def s3_compatible(cls) -> "StorageCapabilities":
51
- """Capabilities for S3-compatible backends."""
52
- return cls(
53
- is_cloud_native=True,
54
- supports_multipart_upload=True,
55
- supports_s3_select=True,
56
- supports_arrow=True,
57
- supports_streaming=True,
58
- supports_async=True,
59
- )
60
-
61
- @classmethod
62
- def gcs(cls) -> "StorageCapabilities":
63
- """Capabilities for Google Cloud Storage."""
64
- return cls(
65
- is_cloud_native=True,
66
- supports_multipart_upload=True,
67
- supports_gcs_compose=True,
68
- supports_arrow=True,
69
- supports_streaming=True,
70
- supports_async=True,
71
- )
72
-
73
- @classmethod
74
- def azure_blob(cls) -> "StorageCapabilities":
75
- """Capabilities for Azure Blob Storage."""
76
- return cls(
77
- is_cloud_native=True,
78
- supports_multipart_upload=True,
79
- supports_azure_snapshots=True,
80
- supports_arrow=True,
81
- supports_streaming=True,
82
- supports_async=True,
83
- )
84
-
85
-
86
- @mypyc_attr(allow_interpreted_subclasses=True)
87
- class HasStorageCapabilities:
88
- """Mixin for storage backends that expose their capabilities."""
89
-
90
- __slots__ = ()
91
-
92
- capabilities: ClassVar[StorageCapabilities]
93
-
94
- @classmethod
95
- def has_capability(cls, capability: str) -> bool:
96
- """Check if backend has a specific capability."""
97
- return getattr(cls.capabilities, capability, False)
98
-
99
- @classmethod
100
- def get_capabilities(cls) -> StorageCapabilities:
101
- """Get all capabilities for this backend."""
102
- return cls.capabilities