pyspiral 0.4.0__pp310-pypy310_pp73-macosx_10_12_x86_64.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.
Files changed (98) hide show
  1. pyspiral-0.4.0.dist-info/METADATA +46 -0
  2. pyspiral-0.4.0.dist-info/RECORD +98 -0
  3. pyspiral-0.4.0.dist-info/WHEEL +4 -0
  4. pyspiral-0.4.0.dist-info/entry_points.txt +2 -0
  5. spiral/__init__.py +10 -0
  6. spiral/_lib.pypy310-pp73-darwin.so +0 -0
  7. spiral/adbc.py +393 -0
  8. spiral/api/__init__.py +64 -0
  9. spiral/api/admin.py +15 -0
  10. spiral/api/client.py +160 -0
  11. spiral/api/filesystems.py +153 -0
  12. spiral/api/organizations.py +77 -0
  13. spiral/api/projects.py +197 -0
  14. spiral/api/telemetry.py +19 -0
  15. spiral/api/types.py +20 -0
  16. spiral/api/workloads.py +52 -0
  17. spiral/arrow_.py +221 -0
  18. spiral/cli/__init__.py +79 -0
  19. spiral/cli/__main__.py +4 -0
  20. spiral/cli/admin.py +16 -0
  21. spiral/cli/app.py +65 -0
  22. spiral/cli/console.py +95 -0
  23. spiral/cli/fs.py +112 -0
  24. spiral/cli/iceberg/__init__.py +7 -0
  25. spiral/cli/iceberg/namespaces.py +47 -0
  26. spiral/cli/iceberg/tables.py +60 -0
  27. spiral/cli/indexes/__init__.py +19 -0
  28. spiral/cli/login.py +22 -0
  29. spiral/cli/orgs.py +90 -0
  30. spiral/cli/printer.py +53 -0
  31. spiral/cli/projects.py +136 -0
  32. spiral/cli/state.py +5 -0
  33. spiral/cli/tables/__init__.py +121 -0
  34. spiral/cli/telemetry.py +18 -0
  35. spiral/cli/types.py +51 -0
  36. spiral/cli/workloads.py +59 -0
  37. spiral/client.py +79 -0
  38. spiral/core/__init__.pyi +0 -0
  39. spiral/core/client/__init__.pyi +117 -0
  40. spiral/core/index/__init__.pyi +15 -0
  41. spiral/core/table/__init__.pyi +108 -0
  42. spiral/core/table/manifests/__init__.pyi +35 -0
  43. spiral/core/table/metastore/__init__.pyi +62 -0
  44. spiral/core/table/spec/__init__.pyi +214 -0
  45. spiral/datetime_.py +27 -0
  46. spiral/expressions/__init__.py +245 -0
  47. spiral/expressions/base.py +149 -0
  48. spiral/expressions/http.py +86 -0
  49. spiral/expressions/io.py +100 -0
  50. spiral/expressions/list_.py +68 -0
  51. spiral/expressions/mp4.py +62 -0
  52. spiral/expressions/png.py +18 -0
  53. spiral/expressions/qoi.py +18 -0
  54. spiral/expressions/refs.py +58 -0
  55. spiral/expressions/str_.py +39 -0
  56. spiral/expressions/struct.py +59 -0
  57. spiral/expressions/text.py +62 -0
  58. spiral/expressions/tiff.py +223 -0
  59. spiral/expressions/udf.py +46 -0
  60. spiral/grpc_.py +32 -0
  61. spiral/iceberg/__init__.py +3 -0
  62. spiral/iceberg/client.py +33 -0
  63. spiral/indexes/__init__.py +5 -0
  64. spiral/indexes/client.py +137 -0
  65. spiral/indexes/index.py +34 -0
  66. spiral/indexes/scan.py +22 -0
  67. spiral/project.py +46 -0
  68. spiral/protogen/_/__init__.py +0 -0
  69. spiral/protogen/_/arrow/__init__.py +0 -0
  70. spiral/protogen/_/arrow/flight/__init__.py +0 -0
  71. spiral/protogen/_/arrow/flight/protocol/__init__.py +0 -0
  72. spiral/protogen/_/arrow/flight/protocol/sql/__init__.py +1990 -0
  73. spiral/protogen/_/scandal/__init__.py +178 -0
  74. spiral/protogen/_/spiral/__init__.py +0 -0
  75. spiral/protogen/_/spiral/table/__init__.py +22 -0
  76. spiral/protogen/_/substrait/__init__.py +3399 -0
  77. spiral/protogen/_/substrait/extensions/__init__.py +115 -0
  78. spiral/protogen/__init__.py +0 -0
  79. spiral/protogen/substrait/__init__.py +3399 -0
  80. spiral/protogen/substrait/extensions/__init__.py +115 -0
  81. spiral/protogen/util.py +41 -0
  82. spiral/py.typed +0 -0
  83. spiral/server.py +17 -0
  84. spiral/settings.py +101 -0
  85. spiral/substrait_.py +279 -0
  86. spiral/tables/__init__.py +12 -0
  87. spiral/tables/client.py +130 -0
  88. spiral/tables/dataset.py +250 -0
  89. spiral/tables/debug/__init__.py +0 -0
  90. spiral/tables/debug/manifests.py +70 -0
  91. spiral/tables/debug/metrics.py +56 -0
  92. spiral/tables/debug/scan.py +248 -0
  93. spiral/tables/maintenance.py +12 -0
  94. spiral/tables/scan.py +193 -0
  95. spiral/tables/snapshot.py +78 -0
  96. spiral/tables/table.py +157 -0
  97. spiral/tables/transaction.py +52 -0
  98. spiral/types_.py +6 -0
spiral/client.py ADDED
@@ -0,0 +1,79 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ import jwt
4
+
5
+ from spiral.api import SpiralAPI
6
+ from spiral.api.projects import CreateProjectRequest, CreateProjectResponse
7
+ from spiral.core.client import Spiral as CoreSpiral
8
+ from spiral.settings import Settings, settings
9
+
10
+ if TYPE_CHECKING:
11
+ from spiral.iceberg import Iceberg
12
+ from spiral.project import Project
13
+
14
+
15
+ class Spiral:
16
+ def __init__(self, config: Settings | None = None):
17
+ self._config = config or settings()
18
+ self._api = self._config.api
19
+ self._core = CoreSpiral(
20
+ api_url=self._config.spiraldb.uri, spfs_url=self._config.spfs.uri, authn=self._config.authn
21
+ )
22
+ self._org = None
23
+
24
+ @property
25
+ def config(self) -> Settings:
26
+ return self._config
27
+
28
+ @property
29
+ def api(self) -> SpiralAPI:
30
+ return self._api
31
+
32
+ @property
33
+ def organization(self) -> str:
34
+ if self._org is None:
35
+ token = self._config.authn.token()
36
+ if token is None:
37
+ raise ValueError("Authentication failed.")
38
+ token_payload = jwt.decode(token.expose_secret(), options={"verify_signature": False})
39
+ if "org_id" not in token_payload:
40
+ raise ValueError("Please create an organization.")
41
+ self._org = token_payload["org_id"]
42
+ return self._org
43
+
44
+ def list_projects(self) -> list["Project"]:
45
+ """List project IDs."""
46
+ from .project import Project
47
+
48
+ return [Project(self, id=p.id, name=p.name) for p in self.api.project.list()]
49
+
50
+ def create_project(
51
+ self,
52
+ id_prefix: str | None = None,
53
+ *,
54
+ name: str | None = None,
55
+ ) -> "Project":
56
+ """Create a project in the current, or given, organization."""
57
+ from .project import Project
58
+
59
+ res: CreateProjectResponse = self.api.project.create(CreateProjectRequest(id_prefix=id_prefix, name=name))
60
+ return Project(self, res.project.id, name=res.project.name)
61
+
62
+ def project(self, project_id: str) -> "Project":
63
+ """Open an existing project."""
64
+ from spiral.project import Project
65
+
66
+ # We avoid an API call since we'd just be fetching a human-readable name. Seems a waste in most cases.
67
+ return Project(self, id=project_id, name=project_id)
68
+
69
+ @property
70
+ def iceberg(self) -> "Iceberg":
71
+ """
72
+ Apache Iceberg is a powerful open-source table format designed for high-performance data lakes.
73
+ Iceberg brings reliability, scalability, and advanced features like time travel, schema evolution,
74
+ and ACID transactions to your warehouse.
75
+
76
+ """
77
+ from spiral.iceberg import Iceberg
78
+
79
+ return Iceberg(self)
File without changes
@@ -0,0 +1,117 @@
1
+ from spiral.api.types import DatasetName, IndexName, OrgId, ProjectId, RootUri, TableName
2
+ from spiral.core.index import SearchScan, TextIndex
3
+ from spiral.core.table import Table, TableMaintenance, TableScan, TableSnapshot, TableTransaction
4
+ from spiral.core.table.spec import Schema
5
+ from spiral.expressions import Expr
6
+
7
+ class Token:
8
+ def __init__(self, value: str): ...
9
+ def expose_secret(self) -> str: ...
10
+
11
+ class Authn:
12
+ @staticmethod
13
+ def from_token(token: Token) -> Authn: ...
14
+ @staticmethod
15
+ def from_fallback() -> Authn: ...
16
+ @staticmethod
17
+ def from_device() -> Authn: ...
18
+ def token(self) -> Token | None: ...
19
+
20
+ class DeviceCodeAuth:
21
+ @staticmethod
22
+ def default() -> DeviceCodeAuth:
23
+ """Return the static device code instance."""
24
+ ...
25
+ def authenticate(self, force: bool = False, org_id: OrgId | None = None) -> Token:
26
+ """Authenticate using device code flow."""
27
+ ...
28
+
29
+ def logout(self) -> None:
30
+ """Logout from the device authentication session."""
31
+ ...
32
+
33
+ class Spiral:
34
+ """A client for Spiral database"""
35
+ def __init__(
36
+ self,
37
+ api_url: str | None = None,
38
+ spfs_url: str | None = None,
39
+ authn: Authn | None = None,
40
+ ):
41
+ """Initialize the Spiral client."""
42
+ ...
43
+ def authn(self) -> Authn:
44
+ """Get the current authentication context."""
45
+ ...
46
+ def create_table(
47
+ self,
48
+ project_id: ProjectId,
49
+ dataset: DatasetName,
50
+ table: TableName,
51
+ key_schema: Schema,
52
+ *,
53
+ root_uri: RootUri | None = None,
54
+ exist_ok: bool = False,
55
+ ) -> Table:
56
+ """Create a new table in the specified project."""
57
+ ...
58
+
59
+ def get_table(self, table_id: str) -> Table:
60
+ """Get and open table."""
61
+
62
+ def open_table(self, table_id: str, key_schema: Schema, root_uri: RootUri) -> Table:
63
+ """Open a table. This does not make any network calls."""
64
+ ...
65
+
66
+ def open_table_scan(
67
+ self,
68
+ projection: Expr,
69
+ filter: Expr | None = None,
70
+ asof: int | None = None,
71
+ exclude_keys: bool = False,
72
+ ) -> TableScan:
73
+ """Construct a table scan."""
74
+ ...
75
+
76
+ def open_transaction(self, table: Table, format: str | None = None) -> TableTransaction:
77
+ """Being transaction."""
78
+ ...
79
+
80
+ def open_maintenance(self, table: Table, format: str | None = None) -> TableMaintenance:
81
+ """Access maintenance operations for a table."""
82
+ ...
83
+ def create_text_index(
84
+ self,
85
+ project_id: ProjectId,
86
+ name: IndexName,
87
+ projection: Expr,
88
+ filter: Expr | None = None,
89
+ *,
90
+ root_uri: RootUri | None = None,
91
+ exist_ok: bool = False,
92
+ ) -> TextIndex:
93
+ """Create a new index in the specified project."""
94
+ ...
95
+
96
+ def get_text_index(self, index_id: str) -> TextIndex:
97
+ """Get a text-based index."""
98
+ ...
99
+
100
+ def open_search_scan(
101
+ self,
102
+ rank_by: Expr,
103
+ top_k: int,
104
+ # NOTE(marko): Required for now.
105
+ freshness_window_s: int,
106
+ *,
107
+ filter: Expr | None = None,
108
+ ) -> SearchScan:
109
+ """Query an index."""
110
+ ...
111
+
112
+ def _sync_snapshot(self, index_id: str, snapshot: TableSnapshot) -> None:
113
+ """Synchronize an index with a table snapshot.
114
+
115
+ IMPORTANT: This is only exposed for testing purposes and should not be used.
116
+ """
117
+ ...
@@ -0,0 +1,15 @@
1
+ import pyarrow as pa
2
+
3
+ class IndexStatus:
4
+ status: str
5
+ staleness_s: int | None
6
+ # An extent of keys that are indexed.
7
+ # key_extent: KeyExtent | None
8
+
9
+ class TextIndex:
10
+ id: str
11
+
12
+ def status(self) -> IndexStatus: ...
13
+
14
+ class SearchScan:
15
+ def to_record_batches(self) -> pa.RecordBatchReader: ...
@@ -0,0 +1,108 @@
1
+ from typing import Any, Literal
2
+
3
+ import pyarrow as pa
4
+ from spiral.expressions import Expr
5
+
6
+ from .manifests import FragmentManifest
7
+ from .metastore import PyMetastore
8
+ from .spec import ColumnGroup, Key, Schema, WriteAheadLog
9
+
10
+ class KeyRange:
11
+ """A right-exclusive range of keys."""
12
+
13
+ def __init__(self, *, begin: Key, end: Key): ...
14
+
15
+ begin: Key
16
+ end: Key
17
+
18
+ def union(self, other: KeyRange) -> KeyRange: ...
19
+ def __or__(self, other: KeyRange) -> KeyRange: ...
20
+ def intersection(self, key_extent: KeyRange) -> KeyRange | None: ...
21
+ def __and__(self, other: KeyRange) -> KeyRange | None: ...
22
+ def contains(self, item: Key) -> bool: ...
23
+ def __contains__(self, item: Key) -> bool: ...
24
+ def is_disjoint(self, key_range: KeyRange) -> bool:
25
+ return self.end <= key_range.begin or self.begin >= key_range.end
26
+
27
+ @staticmethod
28
+ def beginning_with(begin: Key) -> KeyRange: ...
29
+ @staticmethod
30
+ def ending_with(end: Key) -> KeyRange: ...
31
+ @staticmethod
32
+ def full() -> KeyRange: ...
33
+
34
+ class Table:
35
+ def __init__(self, metastore: PyMetastore): ...
36
+
37
+ id: str
38
+ root_uri: str
39
+ key_schema: Schema
40
+ metastore: PyMetastore
41
+
42
+ def get_wal(self, *, asof: int | None) -> WriteAheadLog: ...
43
+ def get_schema(self, *, asof: int | None) -> Schema: ...
44
+ def get_snapshot(self, *, asof: int | None) -> TableSnapshot: ...
45
+
46
+ class TableSnapshot:
47
+ """A snapshot of a table at a specific point in time."""
48
+
49
+ asof: int
50
+ table: Table
51
+ wal: WriteAheadLog
52
+
53
+ class TableScan:
54
+ def key_schema(self) -> Schema: ...
55
+ def schema(self) -> Schema: ...
56
+ def is_empty(self) -> bool: ...
57
+ def split(self) -> list[KeyRange]: ...
58
+ def table_ids(self) -> list[str]: ...
59
+ def column_groups(self) -> list[ColumnGroup]: ...
60
+ def to_record_batches(
61
+ self,
62
+ key_table: pa.Table | pa.RecordBatch | None = None,
63
+ batch_readahead: int | None = None,
64
+ ) -> pa.RecordBatchReader: ...
65
+ def to_shuffled_record_batches(
66
+ self,
67
+ batch_readahead: int | None = None,
68
+ shuffle_buffer_size: int | None = None,
69
+ shuffle_pool_num_rows: int | None = None,
70
+ ) -> pa.RecordBatchReader: ...
71
+ def column_group_scan(self, column_group: ColumnGroup) -> ColumnGroupScan: ...
72
+ def key_space_scan(self, table_id: str) -> KeySpaceScan: ...
73
+ def metrics(self) -> dict[str, Any]: ...
74
+
75
+ class KeySpaceScan:
76
+ manifest: FragmentManifest
77
+
78
+ def key_schema(self) -> Schema: ...
79
+
80
+ class ColumnGroupScan:
81
+ manifest: FragmentManifest
82
+
83
+ def schema(self) -> Schema: ...
84
+
85
+ class TableTransaction:
86
+ status: str
87
+
88
+ def write(self, expr: Expr, *, partition_size_bytes: int | None = None): ...
89
+ def commit(self): ...
90
+ def abort(self): ...
91
+ def metrics(self) -> dict[str, Any]: ...
92
+
93
+ class TableMaintenance:
94
+ def flush_wal(self): ...
95
+ def compact_key_space(
96
+ self,
97
+ *,
98
+ mode: Literal["plan", "read", "write"] | None = None,
99
+ partition_bytes_min: int | None = None,
100
+ ): ...
101
+ def compact_column_group(
102
+ self,
103
+ column_group: ColumnGroup,
104
+ *,
105
+ mode: Literal["plan", "read", "write"] | None = None,
106
+ partition_bytes_min: int | None = None,
107
+ ): ...
108
+ def metrics(self) -> dict[str, Any]: ...
@@ -0,0 +1,35 @@
1
+ import pyarrow as pa
2
+ from spiral.core.table import KeyRange
3
+ from spiral.core.table.spec import FileFormat, FragmentLevel, KeyExtent, KeySpan
4
+ from spiral.types_ import Timestamp
5
+
6
+ class FragmentManifest:
7
+ def __len__(self): ...
8
+ def __getitem__(self, idx: int): ...
9
+ def to_arrow(self) -> pa.RecordBatchReader: ...
10
+ @staticmethod
11
+ def compute_schema(data_schema: pa.Schema) -> pa.Schema: ...
12
+ @staticmethod
13
+ def from_fragment(fragment_file: FragmentFile) -> FragmentManifest: ...
14
+ @staticmethod
15
+ def from_arrow(reader: pa.RecordBatchReader) -> FragmentManifest: ...
16
+ @staticmethod
17
+ def empty() -> FragmentManifest: ...
18
+
19
+ class FragmentFile:
20
+ id: str
21
+ committed_at: Timestamp | None
22
+ compacted_at: Timestamp | None
23
+ format: FileFormat
24
+ format_metadata: bytes | None
25
+ size_bytes: int
26
+ column_ids: list[str]
27
+ level: FragmentLevel
28
+ # NOTE: Empty for key space files.
29
+ column_ids: list[str]
30
+ ks_id: str
31
+ key_span: KeySpan
32
+ key_extent: KeyExtent
33
+
34
+ @property
35
+ def key_range(self) -> KeyRange: ...
@@ -0,0 +1,62 @@
1
+ """The SpiralDB metastore API."""
2
+
3
+ from spiral.core.client import Authn
4
+ from spiral.core.table.spec import ColumnGroup, ColumnGroupMetadata, FileFormat, KeySpaceMetadata, Schema, WriteAheadLog
5
+ from spiral.types_ import Uri
6
+
7
+ class FileHandle:
8
+ def __init__(self, *, uri: str, format: FileFormat, spfs_token: str | None): ...
9
+
10
+ uri: str
11
+ format: FileFormat
12
+ spfs_token: str | None
13
+
14
+ class FileRef:
15
+ def __init__(self, *, id: str, file_type: FileType, file_format: FileFormat): ...
16
+
17
+ id: str
18
+ file_type: FileType
19
+ file_format: FileFormat
20
+
21
+ def resolve_uri(self, root_uri: str) -> str:
22
+ """Resolves the file reference URI given the root URI."""
23
+
24
+ class FileType:
25
+ FragmentFile: FileType
26
+ FragmentManifest: FileType
27
+ ReferenceFile: FileType
28
+
29
+ def __int__(self) -> int:
30
+ """Returns the protobuf enum int value."""
31
+
32
+ class PyMetastore:
33
+ """Rust implementation of the metastore API."""
34
+
35
+ @property
36
+ def table_id(self) -> str: ...
37
+ @property
38
+ def root_uri(self) -> Uri: ...
39
+ @property
40
+ def key_schema(self) -> Schema: ...
41
+ def get_wal(self) -> WriteAheadLog:
42
+ """Return the log for the table."""
43
+ def get_key_space_metadata(self) -> KeySpaceMetadata:
44
+ """Return the metadata for the key space."""
45
+ ...
46
+ def get_column_group_metadata(self, column_group: ColumnGroup) -> ColumnGroupMetadata:
47
+ """Return the metadata for a column group."""
48
+ ...
49
+
50
+ @staticmethod
51
+ def http(
52
+ table_id: str,
53
+ root_uri: str,
54
+ key_schema: Schema,
55
+ base_url: str,
56
+ authn: Authn,
57
+ ) -> PyMetastore:
58
+ """Construct a PyMetastore backed by an HTTP metastore service."""
59
+
60
+ @staticmethod
61
+ def test(table_id: str, root_uri: str, key_schema: Schema) -> PyMetastore:
62
+ """Construct a PyMetastore backed by an in-memory mock metastore service."""
@@ -0,0 +1,214 @@
1
+ """Type definitions for the spiral.core.spec module shipped as part of the native library."""
2
+
3
+ import pyarrow as pa
4
+
5
+ class ColumnGroup:
6
+ def __init__(self, path: list[str]): ...
7
+ @property
8
+ def table_id(self) -> str: ...
9
+ @property
10
+ def path(self) -> list[str]: ...
11
+ def identifier(self, salt: int) -> str:
12
+ """Return the column group identifier based on the given salt."""
13
+
14
+ @staticmethod
15
+ def from_str(path: str) -> ColumnGroup: ...
16
+
17
+ class KeySpaceMetadata:
18
+ def __init__(
19
+ self,
20
+ *,
21
+ manifest_handle: ManifestHandle | None,
22
+ last_modified_at: int,
23
+ ): ...
24
+
25
+ manifest_handle: ManifestHandle | None
26
+ last_modified_at: int
27
+
28
+ def asof(self, asof: int) -> KeySpaceMetadata:
29
+ """Returns the metadata as of a given timestamp. Currently just filtering versioned schemas."""
30
+ ...
31
+
32
+ def apply_wal(self, wal: WriteAheadLog) -> KeySpaceMetadata:
33
+ """Applies the given WAL to the metadata."""
34
+
35
+ class ColumnGroupMetadata:
36
+ def __init__(
37
+ self,
38
+ *,
39
+ column_group: ColumnGroup,
40
+ manifest_handle: ManifestHandle | None,
41
+ last_modified_at: int,
42
+ schema_versions: list[VersionedSchema] | None,
43
+ immutable_schema: bool,
44
+ schema_salt: int,
45
+ ): ...
46
+
47
+ column_group: ColumnGroup
48
+ manifest_handle: ManifestHandle | None
49
+ last_modified_at: int
50
+ schema_versions: list[VersionedSchema]
51
+ immutable_schema: bool
52
+ schema_salt: int
53
+
54
+ def latest_schema(self) -> VersionedSchema:
55
+ """Returns the latest schema of the column group."""
56
+ ...
57
+
58
+ def asof(self, asof: int) -> ColumnGroupMetadata:
59
+ """Returns the metadata as of a given timestamp. Currently just filtering versioned schemas."""
60
+ ...
61
+
62
+ def apply_wal(self, wal: WriteAheadLog) -> ColumnGroupMetadata:
63
+ """Applies the given WAL to the metadata."""
64
+
65
+ class LogEntry:
66
+ ts: int
67
+ operation: (
68
+ KeySpaceWriteOp
69
+ | ColumnGroupWriteOp
70
+ | SchemaEvolutionOp
71
+ | SchemaBreakOp
72
+ | KeySpaceCompactOp
73
+ | ColumnGroupCompactOp
74
+ )
75
+
76
+ def column_group(self) -> ColumnGroup | None:
77
+ """Returns the column group of the entry if it is associated with one."""
78
+
79
+ class FileFormat:
80
+ def __init__(self, value: int): ...
81
+
82
+ Parquet: FileFormat
83
+ Protobuf: FileFormat
84
+ BinaryArray: FileFormat
85
+ Vortex: FileFormat
86
+
87
+ def __int__(self) -> int:
88
+ """Returns the protobuf enum int value."""
89
+ ...
90
+
91
+ def __str__(self) -> str:
92
+ """Returns the string representation of the file format."""
93
+ ...
94
+
95
+ class FragmentLevel:
96
+ L0: FragmentLevel
97
+ L1: FragmentLevel
98
+
99
+ def __int__(self) -> int:
100
+ """Returns the protobuf enum int value."""
101
+ ...
102
+
103
+ class Key:
104
+ key: bytes
105
+
106
+ def __init__(self, key: bytes): ...
107
+ def __bytes__(self): ...
108
+ def step(self) -> Key:
109
+ """Returns the next key in the key space."""
110
+
111
+ @staticmethod
112
+ def min() -> Key: ...
113
+ @staticmethod
114
+ def max() -> Key: ...
115
+
116
+ class KeyExtent:
117
+ """An inclusive range of keys."""
118
+
119
+ def __init__(self, *, min: Key, max: Key): ...
120
+
121
+ min: Key
122
+ max: Key
123
+
124
+ def union(self, key_extent: KeyExtent) -> KeyExtent: ...
125
+ def __or__(self, other: KeyExtent) -> KeyExtent: ...
126
+ def intersection(self, key_extent: KeyExtent) -> KeyExtent | None: ...
127
+ def __and__(self, other: KeyExtent) -> KeyExtent | None: ...
128
+ def contains(self, item: Key) -> bool: ...
129
+ def __contains__(self, item: Key) -> bool: ...
130
+
131
+ class KeySpan:
132
+ """An exclusive range of keys as indexed by their position in a key space."""
133
+
134
+ def __init__(self, *, begin: int, end: int): ...
135
+
136
+ begin: int
137
+ end: int
138
+
139
+ def __len__(self) -> int: ...
140
+ def shift(self, offset: int) -> KeySpan: ...
141
+ def union(self, other: KeySpan) -> KeySpan: ...
142
+ def __or__(self, other: KeySpan) -> KeySpan: ...
143
+
144
+ class ManifestHandle:
145
+ id: str
146
+ format: FileFormat
147
+ file_size: int
148
+
149
+ class Schema:
150
+ def to_arrow(self) -> pa.Schema:
151
+ """Returns the Arrow schema."""
152
+ ...
153
+ @staticmethod
154
+ def from_arrow(arrow: pa.Schema) -> Schema:
155
+ """Creates a Schema from an Arrow schema."""
156
+ ...
157
+ def __len__(self):
158
+ """Returns the number of columns in the schema."""
159
+ ...
160
+ @property
161
+ def names(self) -> list[str]:
162
+ """Returns the names of the columns in the schema."""
163
+ ...
164
+
165
+ class VersionedSchema:
166
+ ts: int
167
+ schema: Schema
168
+ column_ids: list[str]
169
+
170
+ class KeySpaceWriteOp:
171
+ ks_id: str
172
+ manifest_handle: ManifestHandle
173
+
174
+ class ColumnGroupWriteOp:
175
+ column_group: ColumnGroup
176
+ level: FragmentLevel
177
+ manifest_handle: ManifestHandle
178
+ key_span: KeySpan
179
+ key_extent: KeyExtent
180
+ column_ids: list[str]
181
+
182
+ class SchemaEvolutionOp:
183
+ column_group: ColumnGroup
184
+
185
+ class SchemaBreakOp:
186
+ column_group: ColumnGroup
187
+
188
+ class KeySpaceCompactOp:
189
+ ks_ids: list[str]
190
+ moved_ks_ids: list[str]
191
+
192
+ class ColumnGroupCompactOp:
193
+ column_group: ColumnGroup
194
+ fragment_ids: list[int]
195
+
196
+ class WriteAheadLog:
197
+ def __init__(
198
+ self,
199
+ *,
200
+ entries: list[LogEntry] | None = None,
201
+ truncated_up_to: int = 0,
202
+ ): ...
203
+
204
+ entries: list[LogEntry]
205
+ truncated_up_to: int
206
+
207
+ @property
208
+ def last_modified_at(self) -> int:
209
+ """Returns the timestamp of the last modification of the log."""
210
+
211
+ def filter(
212
+ self, asof: int | None = None, since: int | None = None, column_group: ColumnGroup | None = None
213
+ ) -> WriteAheadLog:
214
+ """Filters the WAL to entries by the given parameters."""
spiral/datetime_.py ADDED
@@ -0,0 +1,27 @@
1
+ import warnings
2
+ from datetime import datetime, timedelta, timezone, tzinfo
3
+
4
+ _THE_EPOCH = datetime.fromtimestamp(0, tz=timezone.utc)
5
+
6
+
7
+ def local_tz() -> tzinfo:
8
+ """Determine this machine's local timezone."""
9
+ tz = datetime.now().astimezone().tzinfo
10
+ if tz is None:
11
+ raise ValueError("Could not determine this machine's local timezone.")
12
+ return tz
13
+
14
+
15
+ def timestamp_micros(instant: datetime) -> int:
16
+ """The number of microseconds between the epoch and the given instant."""
17
+ if instant.tzinfo is None:
18
+ warnings.warn("assuming timezone-naive datetime is local time", stacklevel=2)
19
+ instant = instant.replace(tzinfo=local_tz())
20
+ return (instant - _THE_EPOCH) // timedelta(microseconds=1)
21
+
22
+
23
+ def from_timestamp_micros(ts: int) -> datetime:
24
+ """Convert a timestamp in microseconds to a datetime."""
25
+ if ts < 0:
26
+ raise ValueError("Timestamp must be non-negative")
27
+ return _THE_EPOCH + timedelta(microseconds=ts)