sfs-v2 0.0.1.dev1__py3-none-win_amd64.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.
- sfs/__init__.py +95 -0
- sfs/_native.py +417 -0
- sfs/async_.py +735 -0
- sfs/errors.py +190 -0
- sfs/experimental.py +51 -0
- sfs/models.py +239 -0
- sfs/native/win32-x86_64/sfs_ffi.dll +0 -0
- sfs/py.typed +0 -0
- sfs/sync.py +772 -0
- sfs_v2-0.0.1.dev1.dist-info/METADATA +208 -0
- sfs_v2-0.0.1.dev1.dist-info/RECORD +13 -0
- sfs_v2-0.0.1.dev1.dist-info/WHEEL +5 -0
- sfs_v2-0.0.1.dev1.dist-info/top_level.txt +1 -0
sfs/__init__.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""SqlFileSystem Python SDK.
|
|
2
|
+
|
|
3
|
+
Only names listed in ``__all__`` are part of the stable Python-first contract.
|
|
4
|
+
Backend experiments live in :mod:`sfs.experimental`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .async_ import AsyncSfs, AsyncSfsScan
|
|
8
|
+
from .errors import (
|
|
9
|
+
ErrorCode,
|
|
10
|
+
SfsAbiMismatchError,
|
|
11
|
+
SfsClosedError,
|
|
12
|
+
SfsCorruptDataError,
|
|
13
|
+
SfsDatabaseError,
|
|
14
|
+
SfsDatabaseLockedError,
|
|
15
|
+
SfsError,
|
|
16
|
+
SfsCompatibilityError,
|
|
17
|
+
SfsDictionaryError,
|
|
18
|
+
SfsInternalError,
|
|
19
|
+
SfsInvalidArgumentError,
|
|
20
|
+
SfsIoError,
|
|
21
|
+
SfsNotFoundError,
|
|
22
|
+
SfsPreconditionError,
|
|
23
|
+
SfsQueueClosedError,
|
|
24
|
+
SfsQueueError,
|
|
25
|
+
SfsQueuePanicError,
|
|
26
|
+
SfsReadonlyError,
|
|
27
|
+
SfsResourceExhaustedError,
|
|
28
|
+
SfsUnsupportedError,
|
|
29
|
+
)
|
|
30
|
+
from .models import (
|
|
31
|
+
AsyncOptions,
|
|
32
|
+
CursorPage,
|
|
33
|
+
DirectoryInfo,
|
|
34
|
+
FileInfo,
|
|
35
|
+
OffsetPage,
|
|
36
|
+
OpenOptions,
|
|
37
|
+
ReadResult,
|
|
38
|
+
RuntimeInfo,
|
|
39
|
+
ScanError,
|
|
40
|
+
ScanErrorMode,
|
|
41
|
+
ScanFilter,
|
|
42
|
+
ScanOptions,
|
|
43
|
+
ScanOrder,
|
|
44
|
+
ScanStats,
|
|
45
|
+
ScannedFile,
|
|
46
|
+
ShrinkReport,
|
|
47
|
+
WriteItem,
|
|
48
|
+
)
|
|
49
|
+
from .sync import Sfs, SfsScan
|
|
50
|
+
|
|
51
|
+
__version__ = "0.0.1.dev1"
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"AsyncOptions",
|
|
55
|
+
"AsyncSfs",
|
|
56
|
+
"AsyncSfsScan",
|
|
57
|
+
"CursorPage",
|
|
58
|
+
"DirectoryInfo",
|
|
59
|
+
"ErrorCode",
|
|
60
|
+
"FileInfo",
|
|
61
|
+
"OffsetPage",
|
|
62
|
+
"OpenOptions",
|
|
63
|
+
"ReadResult",
|
|
64
|
+
"RuntimeInfo",
|
|
65
|
+
"ScanError",
|
|
66
|
+
"ScanErrorMode",
|
|
67
|
+
"ScanFilter",
|
|
68
|
+
"ScanOptions",
|
|
69
|
+
"ScanOrder",
|
|
70
|
+
"ScanStats",
|
|
71
|
+
"ScannedFile",
|
|
72
|
+
"Sfs",
|
|
73
|
+
"SfsAbiMismatchError",
|
|
74
|
+
"SfsClosedError",
|
|
75
|
+
"SfsCorruptDataError",
|
|
76
|
+
"SfsDatabaseError",
|
|
77
|
+
"SfsCompatibilityError",
|
|
78
|
+
"SfsDatabaseLockedError",
|
|
79
|
+
"SfsDictionaryError",
|
|
80
|
+
"SfsError",
|
|
81
|
+
"SfsInternalError",
|
|
82
|
+
"SfsInvalidArgumentError",
|
|
83
|
+
"SfsIoError",
|
|
84
|
+
"SfsNotFoundError",
|
|
85
|
+
"SfsPreconditionError",
|
|
86
|
+
"SfsQueueClosedError",
|
|
87
|
+
"SfsQueueError",
|
|
88
|
+
"SfsQueuePanicError",
|
|
89
|
+
"SfsReadonlyError",
|
|
90
|
+
"SfsResourceExhaustedError",
|
|
91
|
+
"SfsScan",
|
|
92
|
+
"SfsUnsupportedError",
|
|
93
|
+
"ShrinkReport",
|
|
94
|
+
"WriteItem",
|
|
95
|
+
]
|
sfs/_native.py
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
"""Private ctypes bridge. Nothing in this module is a public SDK contract."""
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import sys
|
|
7
|
+
import threading
|
|
8
|
+
from ctypes import (
|
|
9
|
+
POINTER,
|
|
10
|
+
Structure,
|
|
11
|
+
c_char_p,
|
|
12
|
+
c_int32,
|
|
13
|
+
c_int64,
|
|
14
|
+
c_size_t,
|
|
15
|
+
c_uint32,
|
|
16
|
+
c_uint64,
|
|
17
|
+
c_uint8,
|
|
18
|
+
c_void_p,
|
|
19
|
+
)
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
from .errors import ErrorCode, SfsAbiMismatchError, SfsError, error_from_native
|
|
23
|
+
|
|
24
|
+
ABI_VERSION = 2
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _SfsOpenOptionsC(Structure):
|
|
28
|
+
_fields_ = [
|
|
29
|
+
("abi_version", c_uint32),
|
|
30
|
+
("struct_size", c_uint32),
|
|
31
|
+
("readonly", c_int32),
|
|
32
|
+
("max_vol_size_mb", c_uint64),
|
|
33
|
+
("write_flush_ms", c_uint64),
|
|
34
|
+
("write_batch_max", c_size_t),
|
|
35
|
+
("enable_write_queue", c_int32),
|
|
36
|
+
("compression_level", c_int32),
|
|
37
|
+
("enable_dict_training", c_int32),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _SfsPageC(Structure):
|
|
42
|
+
_fields_ = [
|
|
43
|
+
("limit", c_int64),
|
|
44
|
+
("offset", c_int64),
|
|
45
|
+
("start", c_char_p),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class _SfsFileInfoC(Structure):
|
|
50
|
+
_fields_ = [
|
|
51
|
+
("full_path", c_char_p),
|
|
52
|
+
("parent_path", c_char_p),
|
|
53
|
+
("file_name", c_char_p),
|
|
54
|
+
("size", c_int64),
|
|
55
|
+
("storage_position", c_char_p),
|
|
56
|
+
("file_uuid", c_char_p),
|
|
57
|
+
("created_at", c_char_p),
|
|
58
|
+
("updated_at", c_char_p),
|
|
59
|
+
("is_file", c_int32),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class _SfsDirInfoC(Structure):
|
|
64
|
+
_fields_ = [
|
|
65
|
+
("file_count", c_uint64),
|
|
66
|
+
("directory_count", c_uint64),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class _SfsShrinkReportC(Structure):
|
|
71
|
+
"""Legacy 24-byte report used by the required ``sfs_shrink`` symbol."""
|
|
72
|
+
|
|
73
|
+
_fields_ = [
|
|
74
|
+
("volumes_scanned", c_uint64),
|
|
75
|
+
("rows_deleted", c_uint64),
|
|
76
|
+
("bytes_reclaimed", c_uint64),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _SfsShrinkReportV2C(Structure):
|
|
81
|
+
_fields_ = [
|
|
82
|
+
("abi_version", c_uint32),
|
|
83
|
+
("struct_size", c_uint32),
|
|
84
|
+
("volumes_scanned", c_uint64),
|
|
85
|
+
("rows_deleted", c_uint64),
|
|
86
|
+
("bytes_reclaimed", c_uint64),
|
|
87
|
+
("vacuum_failed_vols", POINTER(c_uint32)),
|
|
88
|
+
("vacuum_failed_count", c_size_t),
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class _SfsBufferC(Structure):
|
|
93
|
+
_fields_ = [("data", POINTER(c_uint8)), ("len", c_size_t)]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class _SfsWriteItemC(Structure):
|
|
97
|
+
_fields_ = [
|
|
98
|
+
("path", c_char_p),
|
|
99
|
+
("data", POINTER(c_uint8)),
|
|
100
|
+
("len", c_size_t),
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class _SfsErrorC(Structure):
|
|
105
|
+
_fields_ = [
|
|
106
|
+
("code", c_int32),
|
|
107
|
+
("message", c_char_p),
|
|
108
|
+
("db_code", c_char_p),
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class _SfsScanFilterC(Structure):
|
|
113
|
+
_fields_ = [
|
|
114
|
+
("path_prefix", c_char_p),
|
|
115
|
+
("extensions", POINTER(c_char_p)),
|
|
116
|
+
("n_extensions", c_size_t),
|
|
117
|
+
("name_like", c_char_p),
|
|
118
|
+
("name_prefix", c_char_p),
|
|
119
|
+
("name_suffix", c_char_p),
|
|
120
|
+
("has_size_range", c_int32),
|
|
121
|
+
("size_min", c_int64),
|
|
122
|
+
("size_max", c_int64),
|
|
123
|
+
("updated_after", c_char_p),
|
|
124
|
+
("updated_before", c_char_p),
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class _SfsScanOptionsC(Structure):
|
|
129
|
+
_fields_ = [
|
|
130
|
+
("filter", _SfsScanFilterC),
|
|
131
|
+
("order", c_int32),
|
|
132
|
+
("batch_size", c_size_t),
|
|
133
|
+
("error_mode", c_int32),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class _SfsScanErrorC(Structure):
|
|
138
|
+
_fields_ = [
|
|
139
|
+
("code", c_int32),
|
|
140
|
+
("path", c_char_p),
|
|
141
|
+
("message", c_char_p),
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class _SfsAbiInfoC(Structure):
|
|
146
|
+
_fields_ = [
|
|
147
|
+
("abi_version", c_uint32),
|
|
148
|
+
("struct_size", c_uint32),
|
|
149
|
+
("open_options_size", c_size_t),
|
|
150
|
+
("page_size", c_size_t),
|
|
151
|
+
("file_info_size", c_size_t),
|
|
152
|
+
("dir_info_size", c_size_t),
|
|
153
|
+
("shrink_report_size", c_size_t),
|
|
154
|
+
("buffer_size", c_size_t),
|
|
155
|
+
("write_item_size", c_size_t),
|
|
156
|
+
("error_size", c_size_t),
|
|
157
|
+
("scan_filter_size", c_size_t),
|
|
158
|
+
("scan_options_size", c_size_t),
|
|
159
|
+
("scan_error_size", c_size_t),
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def encode_text(value: str, field: str) -> bytes:
|
|
164
|
+
if not isinstance(value, str):
|
|
165
|
+
raise TypeError("%s must be str" % field)
|
|
166
|
+
if type(value) is not str:
|
|
167
|
+
value = str.__str__(value)
|
|
168
|
+
if "\x00" in value:
|
|
169
|
+
raise ValueError("%s cannot contain NUL" % field)
|
|
170
|
+
return value.encode("utf-8")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def decode_text(value: Optional[bytes]) -> Optional[str]:
|
|
174
|
+
return value.decode("utf-8") if value else None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _platform_directory() -> str:
|
|
178
|
+
platform_name = {"win32": "win32", "linux": "linux", "darwin": "darwin"}.get(sys.platform)
|
|
179
|
+
machine = platform.machine().lower()
|
|
180
|
+
arch = {"x86_64": "x86_64", "amd64": "x86_64", "arm64": "aarch64", "aarch64": "aarch64"}.get(machine)
|
|
181
|
+
if not platform_name or not arch:
|
|
182
|
+
raise RuntimeError("Unsupported platform: %s-%s" % (sys.platform, machine))
|
|
183
|
+
return "%s-%s" % (platform_name, arch)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _library_name() -> str:
|
|
187
|
+
if sys.platform == "win32":
|
|
188
|
+
return "sfs_ffi.dll"
|
|
189
|
+
if sys.platform == "darwin":
|
|
190
|
+
return "libsfs_ffi.dylib"
|
|
191
|
+
return "libsfs_ffi.so"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _library_path() -> str:
|
|
195
|
+
configured = os.environ.get("SFS_NATIVE_PATH")
|
|
196
|
+
if configured:
|
|
197
|
+
return configured
|
|
198
|
+
package_native = os.path.join(os.path.dirname(__file__), "native", _platform_directory(), _library_name())
|
|
199
|
+
if os.path.exists(package_native):
|
|
200
|
+
return package_native
|
|
201
|
+
legacy_native = os.path.join(os.path.dirname(os.path.dirname(__file__)), "native", _platform_directory(), _library_name())
|
|
202
|
+
return legacy_native
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class _NativeApi:
|
|
206
|
+
def __init__(self) -> None:
|
|
207
|
+
try:
|
|
208
|
+
self.lib = ctypes.CDLL(_library_path())
|
|
209
|
+
except OSError as exc:
|
|
210
|
+
raise RuntimeError("Unable to load sfs native library: %s" % exc) from exc
|
|
211
|
+
self._bind()
|
|
212
|
+
self.abi_info = self._verify_abi()
|
|
213
|
+
|
|
214
|
+
def _bind(self) -> None:
|
|
215
|
+
lib = self.lib
|
|
216
|
+
required_symbols = (
|
|
217
|
+
"sfs_abi_version", "sfs_get_abi_info", "sfs_open",
|
|
218
|
+
"sfs_open_postgres", "sfs_open_mysql", "sfs_open_remote",
|
|
219
|
+
"sfs_close_checked", "sfs_stat", "sfs_exists_file",
|
|
220
|
+
"sfs_exists_dir", "sfs_list", "sfs_dir_info", "sfs_fs_info",
|
|
221
|
+
"sfs_read", "sfs_read_batch", "sfs_write", "sfs_write_batch",
|
|
222
|
+
"sfs_scan_open", "sfs_scan_next", "sfs_scan_take_errors",
|
|
223
|
+
"sfs_scan_counts", "sfs_scan_close", "sfs_mkdir", "sfs_delete",
|
|
224
|
+
"sfs_rename", "sfs_shrink", "sfs_free_buffer",
|
|
225
|
+
"sfs_free_buffers", "sfs_free_file_infos",
|
|
226
|
+
"sfs_free_scan_errors", "sfs_error_free",
|
|
227
|
+
)
|
|
228
|
+
missing = [name for name in required_symbols if not hasattr(lib, name)]
|
|
229
|
+
if missing:
|
|
230
|
+
raise SfsAbiMismatchError(
|
|
231
|
+
"native library is missing ABI v2 symbols: " + ", ".join(missing)
|
|
232
|
+
)
|
|
233
|
+
try:
|
|
234
|
+
lib.sfs_abi_version.restype = c_uint32
|
|
235
|
+
lib.sfs_abi_version.argtypes = []
|
|
236
|
+
lib.sfs_get_abi_info.restype = c_int32
|
|
237
|
+
lib.sfs_get_abi_info.argtypes = [POINTER(_SfsAbiInfoC), c_size_t]
|
|
238
|
+
except AttributeError as exc:
|
|
239
|
+
raise SfsAbiMismatchError("native library does not expose the SFS ABI v2 handshake") from exc
|
|
240
|
+
|
|
241
|
+
lib.sfs_open.restype = c_void_p
|
|
242
|
+
lib.sfs_open.argtypes = [c_char_p, POINTER(_SfsOpenOptionsC), POINTER(POINTER(_SfsErrorC))]
|
|
243
|
+
lib.sfs_open_postgres.restype = c_void_p
|
|
244
|
+
lib.sfs_open_postgres.argtypes = [c_char_p, POINTER(_SfsOpenOptionsC), POINTER(POINTER(_SfsErrorC))]
|
|
245
|
+
lib.sfs_open_mysql.restype = c_void_p
|
|
246
|
+
lib.sfs_open_mysql.argtypes = [c_char_p, POINTER(_SfsOpenOptionsC), POINTER(POINTER(_SfsErrorC))]
|
|
247
|
+
lib.sfs_open_remote.restype = c_void_p
|
|
248
|
+
lib.sfs_open_remote.argtypes = [c_char_p, POINTER(POINTER(_SfsErrorC))]
|
|
249
|
+
|
|
250
|
+
# These capabilities were added after the initial ABI v2 release. Keep
|
|
251
|
+
# them optional at load time so the stable local SQLite surface can
|
|
252
|
+
# still diagnose/use an early ABI v2 library. Call sites must handle
|
|
253
|
+
# absence explicitly: token auth reports UNSUPPORTED, while shrink v2
|
|
254
|
+
# deliberately falls back to the documented legacy 24-byte report.
|
|
255
|
+
open_remote_with_token = getattr(lib, "sfs_open_remote_with_token", None)
|
|
256
|
+
if open_remote_with_token is not None:
|
|
257
|
+
open_remote_with_token.restype = c_void_p
|
|
258
|
+
open_remote_with_token.argtypes = [
|
|
259
|
+
c_char_p,
|
|
260
|
+
c_char_p,
|
|
261
|
+
POINTER(POINTER(_SfsErrorC)),
|
|
262
|
+
]
|
|
263
|
+
stream_flush = getattr(lib, "sfs_stream_flush", None)
|
|
264
|
+
if stream_flush is not None:
|
|
265
|
+
stream_flush.restype = c_int32
|
|
266
|
+
stream_flush.argtypes = [c_void_p, POINTER(POINTER(_SfsErrorC))]
|
|
267
|
+
shrink_report_v2_size = getattr(lib, "sfs_shrink_report_v2_size", None)
|
|
268
|
+
shrink_v2 = getattr(lib, "sfs_shrink_v2", None)
|
|
269
|
+
free_u32_array = getattr(lib, "sfs_free_u32_array", None)
|
|
270
|
+
shrink_capabilities = (shrink_report_v2_size, shrink_v2, free_u32_array)
|
|
271
|
+
if not all(capability is not None for capability in shrink_capabilities):
|
|
272
|
+
# Optional extensions are atomic capabilities, not base-ABI requirements. A mixed
|
|
273
|
+
# deployment may briefly expose only part of the trio; disable v2 and retain the
|
|
274
|
+
# required legacy sfs_shrink path rather than making the entire SDK unloadable.
|
|
275
|
+
shrink_report_v2_size = None
|
|
276
|
+
shrink_v2 = None
|
|
277
|
+
free_u32_array = None
|
|
278
|
+
if shrink_v2 is not None:
|
|
279
|
+
shrink_report_v2_size.restype = c_size_t
|
|
280
|
+
shrink_report_v2_size.argtypes = []
|
|
281
|
+
shrink_v2.restype = c_int32
|
|
282
|
+
shrink_v2.argtypes = [
|
|
283
|
+
c_void_p,
|
|
284
|
+
POINTER(_SfsShrinkReportV2C),
|
|
285
|
+
c_size_t,
|
|
286
|
+
POINTER(POINTER(_SfsErrorC)),
|
|
287
|
+
]
|
|
288
|
+
free_u32_array.restype = None
|
|
289
|
+
free_u32_array.argtypes = [POINTER(c_uint32), c_size_t]
|
|
290
|
+
|
|
291
|
+
lib.sfs_close_checked.restype = c_int32
|
|
292
|
+
lib.sfs_close_checked.argtypes = [c_void_p, POINTER(POINTER(_SfsErrorC))]
|
|
293
|
+
|
|
294
|
+
lib.sfs_stat.restype = c_int32
|
|
295
|
+
lib.sfs_stat.argtypes = [c_void_p, c_char_p, POINTER(POINTER(_SfsFileInfoC)), POINTER(POINTER(_SfsErrorC))]
|
|
296
|
+
lib.sfs_exists_file.restype = c_int32
|
|
297
|
+
lib.sfs_exists_file.argtypes = [c_void_p, c_char_p, POINTER(POINTER(_SfsErrorC))]
|
|
298
|
+
lib.sfs_exists_dir.restype = c_int32
|
|
299
|
+
lib.sfs_exists_dir.argtypes = [c_void_p, c_char_p, POINTER(POINTER(_SfsErrorC))]
|
|
300
|
+
lib.sfs_list.restype = c_int32
|
|
301
|
+
lib.sfs_list.argtypes = [c_void_p, c_char_p, POINTER(_SfsPageC), c_char_p, c_int32, POINTER(POINTER(_SfsFileInfoC)), POINTER(c_size_t), POINTER(POINTER(_SfsErrorC))]
|
|
302
|
+
lib.sfs_dir_info.restype = c_int32
|
|
303
|
+
lib.sfs_dir_info.argtypes = [c_void_p, c_char_p, POINTER(_SfsDirInfoC), POINTER(POINTER(_SfsErrorC))]
|
|
304
|
+
lib.sfs_fs_info.restype = c_int32
|
|
305
|
+
lib.sfs_fs_info.argtypes = [c_void_p, POINTER(_SfsDirInfoC), POINTER(POINTER(_SfsErrorC))]
|
|
306
|
+
|
|
307
|
+
lib.sfs_read.restype = c_int32
|
|
308
|
+
lib.sfs_read.argtypes = [c_void_p, c_char_p, POINTER(POINTER(c_uint8)), POINTER(c_size_t), POINTER(POINTER(_SfsErrorC))]
|
|
309
|
+
lib.sfs_read_batch.restype = c_int32
|
|
310
|
+
lib.sfs_read_batch.argtypes = [c_void_p, POINTER(c_char_p), c_size_t, POINTER(POINTER(_SfsBufferC)), POINTER(POINTER(_SfsErrorC))]
|
|
311
|
+
lib.sfs_write.restype = c_int32
|
|
312
|
+
lib.sfs_write.argtypes = [c_void_p, c_char_p, POINTER(c_uint8), c_size_t, POINTER(POINTER(_SfsErrorC))]
|
|
313
|
+
lib.sfs_write_batch.restype = c_int32
|
|
314
|
+
lib.sfs_write_batch.argtypes = [c_void_p, POINTER(_SfsWriteItemC), c_size_t, POINTER(POINTER(_SfsErrorC))]
|
|
315
|
+
|
|
316
|
+
lib.sfs_scan_open.restype = c_void_p
|
|
317
|
+
lib.sfs_scan_open.argtypes = [c_void_p, POINTER(_SfsScanOptionsC), POINTER(POINTER(_SfsErrorC))]
|
|
318
|
+
lib.sfs_scan_next.restype = c_int32
|
|
319
|
+
lib.sfs_scan_next.argtypes = [c_void_p, POINTER(POINTER(_SfsFileInfoC)), POINTER(POINTER(c_uint8)), POINTER(c_size_t), POINTER(POINTER(_SfsErrorC))]
|
|
320
|
+
lib.sfs_scan_take_errors.restype = c_int32
|
|
321
|
+
lib.sfs_scan_take_errors.argtypes = [c_void_p, POINTER(POINTER(_SfsScanErrorC)), POINTER(c_size_t), POINTER(POINTER(_SfsErrorC))]
|
|
322
|
+
lib.sfs_scan_counts.restype = None
|
|
323
|
+
lib.sfs_scan_counts.argtypes = [c_void_p, POINTER(c_uint64), POINTER(c_uint64), POINTER(c_uint64)]
|
|
324
|
+
lib.sfs_scan_close.restype = None
|
|
325
|
+
lib.sfs_scan_close.argtypes = [c_void_p]
|
|
326
|
+
|
|
327
|
+
lib.sfs_mkdir.restype = c_int32
|
|
328
|
+
lib.sfs_mkdir.argtypes = [c_void_p, c_char_p, POINTER(POINTER(_SfsErrorC))]
|
|
329
|
+
lib.sfs_delete.restype = c_int32
|
|
330
|
+
lib.sfs_delete.argtypes = [c_void_p, c_char_p, c_int32, POINTER(POINTER(_SfsErrorC))]
|
|
331
|
+
lib.sfs_rename.restype = c_int32
|
|
332
|
+
lib.sfs_rename.argtypes = [c_void_p, c_char_p, c_char_p, POINTER(POINTER(_SfsErrorC))]
|
|
333
|
+
lib.sfs_shrink.restype = c_int32
|
|
334
|
+
lib.sfs_shrink.argtypes = [c_void_p, POINTER(_SfsShrinkReportC), POINTER(POINTER(_SfsErrorC))]
|
|
335
|
+
|
|
336
|
+
lib.sfs_free_buffer.restype = None
|
|
337
|
+
lib.sfs_free_buffer.argtypes = [POINTER(c_uint8), c_size_t]
|
|
338
|
+
lib.sfs_free_buffers.restype = None
|
|
339
|
+
lib.sfs_free_buffers.argtypes = [POINTER(_SfsBufferC), c_size_t]
|
|
340
|
+
lib.sfs_free_file_infos.restype = None
|
|
341
|
+
lib.sfs_free_file_infos.argtypes = [POINTER(_SfsFileInfoC), c_size_t]
|
|
342
|
+
lib.sfs_free_scan_errors.restype = None
|
|
343
|
+
lib.sfs_free_scan_errors.argtypes = [POINTER(_SfsScanErrorC), c_size_t]
|
|
344
|
+
lib.sfs_error_free.restype = None
|
|
345
|
+
lib.sfs_error_free.argtypes = [POINTER(_SfsErrorC)]
|
|
346
|
+
|
|
347
|
+
def _verify_abi(self) -> _SfsAbiInfoC:
|
|
348
|
+
version = int(self.lib.sfs_abi_version())
|
|
349
|
+
if version != ABI_VERSION:
|
|
350
|
+
raise SfsAbiMismatchError("native ABI version %d does not match Python SDK ABI %d" % (version, ABI_VERSION))
|
|
351
|
+
info = _SfsAbiInfoC()
|
|
352
|
+
code = int(self.lib.sfs_get_abi_info(ctypes.byref(info), ctypes.sizeof(info)))
|
|
353
|
+
if code != 0:
|
|
354
|
+
raise SfsAbiMismatchError("native library rejected ABI info request with code %d" % code)
|
|
355
|
+
if int(info.abi_version) != ABI_VERSION:
|
|
356
|
+
raise SfsAbiMismatchError(
|
|
357
|
+
"ABI info version %d does not match Python SDK ABI %d"
|
|
358
|
+
% (int(info.abi_version), ABI_VERSION)
|
|
359
|
+
)
|
|
360
|
+
if int(info.struct_size) < ctypes.sizeof(_SfsAbiInfoC):
|
|
361
|
+
raise SfsAbiMismatchError(
|
|
362
|
+
"ABI info struct is too small: native=%d python=%d"
|
|
363
|
+
% (int(info.struct_size), ctypes.sizeof(_SfsAbiInfoC))
|
|
364
|
+
)
|
|
365
|
+
expected = {
|
|
366
|
+
"open_options_size": ctypes.sizeof(_SfsOpenOptionsC),
|
|
367
|
+
"page_size": ctypes.sizeof(_SfsPageC),
|
|
368
|
+
"file_info_size": ctypes.sizeof(_SfsFileInfoC),
|
|
369
|
+
"dir_info_size": ctypes.sizeof(_SfsDirInfoC),
|
|
370
|
+
"shrink_report_size": ctypes.sizeof(_SfsShrinkReportC),
|
|
371
|
+
"buffer_size": ctypes.sizeof(_SfsBufferC),
|
|
372
|
+
"write_item_size": ctypes.sizeof(_SfsWriteItemC),
|
|
373
|
+
"error_size": ctypes.sizeof(_SfsErrorC),
|
|
374
|
+
"scan_filter_size": ctypes.sizeof(_SfsScanFilterC),
|
|
375
|
+
"scan_options_size": ctypes.sizeof(_SfsScanOptionsC),
|
|
376
|
+
"scan_error_size": ctypes.sizeof(_SfsScanErrorC),
|
|
377
|
+
}
|
|
378
|
+
mismatches = ["%s python=%d native=%d" % (name, size, int(getattr(info, name))) for name, size in expected.items() if int(getattr(info, name)) != size]
|
|
379
|
+
if mismatches:
|
|
380
|
+
raise SfsAbiMismatchError("native ABI layout mismatch: " + "; ".join(mismatches))
|
|
381
|
+
return info
|
|
382
|
+
|
|
383
|
+
def check_error(
|
|
384
|
+
self,
|
|
385
|
+
code: int,
|
|
386
|
+
err_ref: POINTER(_SfsErrorC),
|
|
387
|
+
*,
|
|
388
|
+
operation: Optional[str] = None,
|
|
389
|
+
path: Optional[str] = None,
|
|
390
|
+
) -> None:
|
|
391
|
+
if code == 0:
|
|
392
|
+
return
|
|
393
|
+
raw_code = code
|
|
394
|
+
message = "Unknown native error (code=%d)" % code
|
|
395
|
+
database_code = None
|
|
396
|
+
if bool(err_ref):
|
|
397
|
+
try:
|
|
398
|
+
error = err_ref.contents
|
|
399
|
+
raw_code = int(error.code)
|
|
400
|
+
message = error.message.decode("utf-8") if error.message else message
|
|
401
|
+
database_code = error.db_code.decode("utf-8") if error.db_code else None
|
|
402
|
+
finally:
|
|
403
|
+
self.lib.sfs_error_free(err_ref)
|
|
404
|
+
raise error_from_native(raw_code, message, operation=operation, path=path, database_code=database_code)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
_native_lock = threading.Lock()
|
|
408
|
+
_native_instance: Optional[_NativeApi] = None
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def get_native() -> _NativeApi:
|
|
412
|
+
global _native_instance
|
|
413
|
+
if _native_instance is None:
|
|
414
|
+
with _native_lock:
|
|
415
|
+
if _native_instance is None:
|
|
416
|
+
_native_instance = _NativeApi()
|
|
417
|
+
return _native_instance
|