pyspiral 0.3.1__cp310-abi3-macosx_11_0_arm64.whl → 0.4.1__cp310-abi3-macosx_11_0_arm64.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.
- {pyspiral-0.3.1.dist-info → pyspiral-0.4.1.dist-info}/METADATA +9 -13
- pyspiral-0.4.1.dist-info/RECORD +98 -0
- {pyspiral-0.3.1.dist-info → pyspiral-0.4.1.dist-info}/WHEEL +1 -1
- spiral/__init__.py +6 -9
- spiral/_lib.abi3.so +0 -0
- spiral/adbc.py +21 -14
- spiral/api/__init__.py +14 -175
- spiral/api/admin.py +12 -26
- spiral/api/client.py +160 -0
- spiral/api/filesystems.py +100 -72
- spiral/api/organizations.py +45 -58
- spiral/api/projects.py +171 -134
- spiral/api/telemetry.py +19 -0
- spiral/api/types.py +20 -0
- spiral/api/workloads.py +32 -25
- spiral/{arrow.py → arrow_.py} +12 -0
- spiral/cli/__init__.py +2 -5
- spiral/cli/admin.py +7 -12
- spiral/cli/app.py +23 -6
- spiral/cli/console.py +1 -1
- spiral/cli/fs.py +82 -17
- spiral/cli/iceberg/__init__.py +7 -0
- spiral/cli/iceberg/namespaces.py +47 -0
- spiral/cli/iceberg/tables.py +60 -0
- spiral/cli/indexes/__init__.py +19 -0
- spiral/cli/login.py +14 -5
- spiral/cli/orgs.py +90 -0
- spiral/cli/printer.py +9 -1
- spiral/cli/projects.py +136 -0
- spiral/cli/state.py +2 -0
- spiral/cli/tables/__init__.py +121 -0
- spiral/cli/telemetry.py +18 -0
- spiral/cli/types.py +8 -10
- spiral/cli/{workload.py → workloads.py} +11 -11
- spiral/{catalog.py → client.py} +23 -37
- spiral/core/client/__init__.pyi +117 -0
- spiral/core/index/__init__.pyi +15 -0
- spiral/core/{core → table}/__init__.pyi +44 -17
- spiral/core/{manifests → table/manifests}/__init__.pyi +5 -23
- spiral/core/table/metastore/__init__.pyi +62 -0
- spiral/core/{spec → table/spec}/__init__.pyi +41 -66
- spiral/datetime_.py +27 -0
- spiral/expressions/__init__.py +26 -18
- spiral/expressions/base.py +5 -5
- spiral/expressions/list_.py +1 -1
- spiral/expressions/mp4.py +2 -9
- spiral/expressions/png.py +1 -1
- spiral/expressions/qoi.py +1 -1
- spiral/expressions/refs.py +3 -9
- spiral/expressions/struct.py +7 -5
- spiral/expressions/text.py +62 -0
- spiral/expressions/udf.py +3 -3
- spiral/iceberg/__init__.py +3 -0
- spiral/iceberg/client.py +33 -0
- spiral/indexes/__init__.py +5 -0
- spiral/indexes/client.py +137 -0
- spiral/indexes/index.py +34 -0
- spiral/indexes/scan.py +22 -0
- spiral/project.py +19 -110
- spiral/{proto → protogen}/_/scandal/__init__.py +23 -135
- spiral/protogen/_/spiral/table/__init__.py +22 -0
- spiral/protogen/substrait/__init__.py +3399 -0
- spiral/protogen/substrait/extensions/__init__.py +115 -0
- spiral/server.py +17 -0
- spiral/settings.py +29 -91
- spiral/substrait_.py +9 -5
- spiral/tables/__init__.py +12 -0
- spiral/tables/client.py +130 -0
- spiral/{dataset.py → tables/dataset.py} +9 -199
- spiral/tables/debug/manifests.py +70 -0
- spiral/tables/debug/metrics.py +56 -0
- spiral/{debug.py → tables/debug/scan.py} +6 -9
- spiral/{maintenance.py → tables/maintenance.py} +1 -1
- spiral/{scan_.py → tables/scan.py} +63 -89
- spiral/tables/snapshot.py +78 -0
- spiral/{table.py → tables/table.py} +59 -73
- spiral/{txn.py → tables/transaction.py} +7 -3
- pyspiral-0.3.1.dist-info/RECORD +0 -85
- spiral/api/tables.py +0 -91
- spiral/api/tokens.py +0 -56
- spiral/authn/authn.py +0 -89
- spiral/authn/device.py +0 -206
- spiral/authn/github_.py +0 -33
- spiral/authn/modal_.py +0 -18
- spiral/cli/org.py +0 -90
- spiral/cli/project.py +0 -109
- spiral/cli/table.py +0 -20
- spiral/cli/token.py +0 -27
- spiral/core/metastore/__init__.pyi +0 -91
- spiral/proto/_/spfs/__init__.py +0 -36
- spiral/proto/_/spiral/table/__init__.py +0 -276
- spiral/proto/_/spiraldb/metastore/__init__.py +0 -499
- spiral/proto/__init__.py +0 -0
- spiral/proto/scandal/__init__.py +0 -45
- spiral/proto/spiral/__init__.py +0 -0
- spiral/proto/spiral/table/__init__.py +0 -96
- {pyspiral-0.3.1.dist-info → pyspiral-0.4.1.dist-info}/entry_points.txt +0 -0
- /spiral/{authn/__init__.py → core/__init__.pyi} +0 -0
- /spiral/{core → protogen/_}/__init__.py +0 -0
- /spiral/{proto/_ → protogen/_/arrow}/__init__.py +0 -0
- /spiral/{proto/_/arrow → protogen/_/arrow/flight}/__init__.py +0 -0
- /spiral/{proto/_/arrow/flight → protogen/_/arrow/flight/protocol}/__init__.py +0 -0
- /spiral/{proto → protogen}/_/arrow/flight/protocol/sql/__init__.py +0 -0
- /spiral/{proto/_/arrow/flight/protocol → protogen/_/spiral}/__init__.py +0 -0
- /spiral/{proto → protogen/_}/substrait/__init__.py +0 -0
- /spiral/{proto → protogen/_}/substrait/extensions/__init__.py +0 -0
- /spiral/{proto/_/spiral → protogen}/__init__.py +0 -0
- /spiral/{proto → protogen}/util.py +0 -0
- /spiral/{proto/_/spiraldb → tables/debug}/__init__.py +0 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from spiral.core.table import TableSnapshot
|
4
|
+
from spiral.expressions import ExprLike
|
5
|
+
from spiral.tables.scan import Scan
|
6
|
+
from spiral.types_ import Timestamp
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
import duckdb
|
10
|
+
import polars as pl
|
11
|
+
import pyarrow.dataset
|
12
|
+
|
13
|
+
from spiral.tables import Tables
|
14
|
+
from spiral.tables.table import Table
|
15
|
+
|
16
|
+
|
17
|
+
class Snapshot:
|
18
|
+
"""Spiral table snapshot.
|
19
|
+
|
20
|
+
A snapshot represents a point-in-time view of a table.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self, tables: "Tables", snapshot: TableSnapshot):
|
24
|
+
self._tables = tables
|
25
|
+
self._snapshot = snapshot
|
26
|
+
|
27
|
+
@property
|
28
|
+
def asof(self) -> Timestamp:
|
29
|
+
"""Returns the asof timestamp of the snapshot."""
|
30
|
+
return self._snapshot.asof
|
31
|
+
|
32
|
+
@property
|
33
|
+
def client(self) -> "Tables":
|
34
|
+
"""Returns the client used by the snapshot."""
|
35
|
+
return self._tables
|
36
|
+
|
37
|
+
@property
|
38
|
+
def table(self) -> "Table":
|
39
|
+
"""Returns the table associated with the snapshot."""
|
40
|
+
from spiral.tables.table import Table
|
41
|
+
|
42
|
+
return Table(self._tables, self._snapshot.table)
|
43
|
+
|
44
|
+
def to_dataset(self) -> "pyarrow.dataset.Dataset":
|
45
|
+
"""Returns a PyArrow Dataset representing the table."""
|
46
|
+
from .dataset import TableDataset
|
47
|
+
|
48
|
+
return TableDataset(self)
|
49
|
+
|
50
|
+
def to_polars(self) -> "pl.LazyFrame":
|
51
|
+
"""Returns a Polars LazyFrame for the Spiral table."""
|
52
|
+
import polars as pl
|
53
|
+
|
54
|
+
return pl.scan_pyarrow_dataset(self.to_dataset())
|
55
|
+
|
56
|
+
def to_duckdb(self) -> "duckdb.DuckDBPyRelation":
|
57
|
+
"""Returns a DuckDB relation for the Spiral table."""
|
58
|
+
import duckdb
|
59
|
+
|
60
|
+
return duckdb.from_arrow(self.to_dataset())
|
61
|
+
|
62
|
+
def scan(
|
63
|
+
self,
|
64
|
+
*projections: ExprLike,
|
65
|
+
where: ExprLike | None = None,
|
66
|
+
exclude_keys: bool = False,
|
67
|
+
) -> Scan:
|
68
|
+
"""Reads the snapshot. If projections are not provided, the entire table is read."""
|
69
|
+
if not projections:
|
70
|
+
# Use table as the default projection.
|
71
|
+
projections = [self._snapshot.table.__expr__]
|
72
|
+
|
73
|
+
return self._tables.scan(
|
74
|
+
*projections,
|
75
|
+
where=where,
|
76
|
+
asof=self._snapshot.asof,
|
77
|
+
exclude_keys=exclude_keys,
|
78
|
+
)
|
@@ -1,22 +1,17 @@
|
|
1
1
|
from datetime import datetime
|
2
|
-
from typing import TYPE_CHECKING
|
2
|
+
from typing import TYPE_CHECKING
|
3
3
|
|
4
|
-
import
|
5
|
-
|
6
|
-
from spiral.core.core import Table as CoreTable
|
7
|
-
from spiral.core.core import TableMaintenance, TableTransaction
|
8
|
-
from spiral.core.spec import Schema
|
4
|
+
from spiral.core.table import Table as CoreTable
|
5
|
+
from spiral.core.table.spec import Schema
|
9
6
|
from spiral.expressions.base import Expr, ExprLike
|
10
|
-
from spiral.
|
11
|
-
from spiral.
|
12
|
-
from spiral.
|
7
|
+
from spiral.settings import settings
|
8
|
+
from spiral.tables.maintenance import Maintenance
|
9
|
+
from spiral.tables.scan import Scan
|
10
|
+
from spiral.tables.snapshot import Snapshot
|
11
|
+
from spiral.tables.transaction import Transaction
|
13
12
|
|
14
13
|
if TYPE_CHECKING:
|
15
|
-
import
|
16
|
-
import polars as pl
|
17
|
-
import pyarrow.dataset
|
18
|
-
|
19
|
-
from spiral.scan_ import Scan
|
14
|
+
from spiral.tables import Tables
|
20
15
|
|
21
16
|
|
22
17
|
class Table(Expr):
|
@@ -25,31 +20,55 @@ class Table(Expr):
|
|
25
20
|
Different catalog implementations should ultimately construct a Table object.
|
26
21
|
"""
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
table: CoreTable,
|
31
|
-
name: str | None = None,
|
32
|
-
):
|
23
|
+
# TODO(marko): Make identifier required.
|
24
|
+
def __init__(self, tables: "Tables", table: CoreTable, *, identifier: str | None = None):
|
33
25
|
super().__init__(table.__expr__)
|
34
26
|
|
27
|
+
self._tables = tables
|
35
28
|
self._table = table
|
36
|
-
self.
|
37
|
-
self._key_schema = self._table.key_schema
|
29
|
+
self._identifier = identifier
|
30
|
+
self._key_schema = self._table.key_schema
|
38
31
|
self._key_columns = set(self._key_schema.names)
|
39
32
|
|
33
|
+
@property
|
34
|
+
def client(self) -> "Tables":
|
35
|
+
"""Returns the client used by the table."""
|
36
|
+
return self._tables
|
37
|
+
|
40
38
|
@property
|
41
39
|
def table_id(self) -> str:
|
42
40
|
return self._table.id
|
43
41
|
|
42
|
+
@property
|
43
|
+
def identifier(self) -> str:
|
44
|
+
"""Returns the fully qualified identifier of the table."""
|
45
|
+
return self._identifier or self._table.id
|
46
|
+
|
47
|
+
@property
|
48
|
+
def dataset(self) -> str | None:
|
49
|
+
"""Returns the dataset of the table."""
|
50
|
+
if self._identifier is None:
|
51
|
+
return None
|
52
|
+
_, dataset, _ = self._identifier.split(".")
|
53
|
+
return dataset
|
54
|
+
|
55
|
+
@property
|
56
|
+
def name(self) -> str | None:
|
57
|
+
"""Returns the name of the table."""
|
58
|
+
if self._identifier is None:
|
59
|
+
return None
|
60
|
+
_, _, name = self._identifier.split(".")
|
61
|
+
return name
|
62
|
+
|
44
63
|
@property
|
45
64
|
def last_modified_at(self) -> int:
|
46
65
|
return self._table.get_wal(asof=None).last_modified_at
|
47
66
|
|
48
67
|
def __str__(self):
|
49
|
-
return self.
|
68
|
+
return self.identifier
|
50
69
|
|
51
70
|
def __repr__(self):
|
52
|
-
return f'Table("{self.
|
71
|
+
return f'Table("{self.identifier}")'
|
53
72
|
|
54
73
|
def __getitem__(self, item: str) -> Expr:
|
55
74
|
from spiral import expressions as se
|
@@ -77,7 +96,7 @@ class Table(Expr):
|
|
77
96
|
return se.merge(se.pack({key: se.key(key) for key in key_paths}), super().select(*other_paths, exclude=exclude))
|
78
97
|
|
79
98
|
@property
|
80
|
-
def key_schema(self) ->
|
99
|
+
def key_schema(self) -> Schema:
|
81
100
|
"""Returns the key schema of the table."""
|
82
101
|
return self._key_schema
|
83
102
|
|
@@ -89,83 +108,50 @@ class Table(Expr):
|
|
89
108
|
"""
|
90
109
|
return self._table.get_schema(asof=None)
|
91
110
|
|
92
|
-
def to_dataset(self) -> "pyarrow.dataset.Dataset":
|
93
|
-
"""Returns a PyArrow Dataset representing the table."""
|
94
|
-
from .dataset import TableDataset
|
95
|
-
|
96
|
-
return TableDataset(self)
|
97
|
-
|
98
|
-
def to_polars(self) -> "pl.LazyFrame":
|
99
|
-
"""Returns a Polars LazyFrame for the Spiral table."""
|
100
|
-
import polars as pl
|
101
|
-
|
102
|
-
return pl.scan_pyarrow_dataset(self.to_dataset())
|
103
|
-
|
104
|
-
def to_duckdb(self) -> "duckdb.DuckDBPyRelation":
|
105
|
-
"""Returns a DuckDB relation for the Spiral table."""
|
106
|
-
import duckdb
|
107
|
-
|
108
|
-
return duckdb.from_arrow(self.to_dataset())
|
109
|
-
|
110
111
|
def scan(
|
111
112
|
self,
|
112
113
|
*projections: ExprLike,
|
113
114
|
where: ExprLike | None = None,
|
114
|
-
asof: datetime | int |
|
115
|
+
asof: datetime | int | None = None,
|
115
116
|
exclude_keys: bool = False,
|
116
|
-
) ->
|
117
|
-
"""Reads the table. If projections are not provided, the entire table is read.
|
118
|
-
|
119
|
-
See `spiral.scan` for more information.
|
120
|
-
"""
|
121
|
-
from spiral.scan_ import scan
|
122
|
-
|
117
|
+
) -> Scan:
|
118
|
+
"""Reads the table. If projections are not provided, the entire table is read."""
|
123
119
|
if not projections:
|
124
120
|
projections = [self]
|
125
121
|
|
126
|
-
return scan(
|
127
|
-
*projections,
|
128
|
-
where=where,
|
129
|
-
asof=asof,
|
130
|
-
exclude_keys=exclude_keys,
|
131
|
-
)
|
122
|
+
return self._tables.scan(*projections, where=where, asof=asof, exclude_keys=exclude_keys)
|
132
123
|
|
133
|
-
# NOTE: "vortex" is valid format. We don't want that visible in the API docs.
|
134
124
|
def write(
|
135
125
|
self,
|
136
126
|
expr: ExprLike,
|
137
127
|
*,
|
138
|
-
format: Literal["parquet"] | None = None,
|
139
128
|
partition_size_bytes: int | None = None,
|
140
129
|
) -> None:
|
141
130
|
"""Write an item to the table inside a single transaction.
|
142
131
|
|
143
132
|
:param expr: The expression to write. Must evaluate to a struct array.
|
144
|
-
:param format: the format to write the data in. Defaults to "parquet".
|
145
133
|
:param partition_size_bytes: The maximum partition size in bytes.
|
146
134
|
"""
|
147
|
-
|
148
|
-
|
149
|
-
with self.txn(format=format) as txn:
|
135
|
+
with self.txn() as txn:
|
150
136
|
txn.write(
|
151
137
|
expr,
|
152
138
|
partition_size_bytes=partition_size_bytes,
|
153
139
|
)
|
154
140
|
|
155
|
-
|
156
|
-
|
141
|
+
def snapshot(self, asof: datetime | int | None = None) -> Snapshot:
|
142
|
+
"""Returns a snapshot of the table at the given timestamp."""
|
143
|
+
if isinstance(asof, datetime):
|
144
|
+
asof = int(asof.timestamp() * 1_000_000)
|
145
|
+
return Snapshot(self._tables, self._table.get_snapshot(asof=asof))
|
146
|
+
|
147
|
+
def txn(self) -> Transaction:
|
157
148
|
"""Begins a new transaction. Transaction must be committed for writes to become visible.
|
158
149
|
|
159
150
|
IMPORTANT: While transaction can be used to atomically write data to the table,
|
160
151
|
it is important that the primary key columns are unique within the transaction.
|
161
|
-
|
162
|
-
:param format: The format to use for the transaction. Defaults to "parquet".
|
163
152
|
"""
|
164
|
-
return Transaction(
|
165
|
-
|
166
|
-
def maintenance(self, format: Literal["parquet"] | None = None) -> Maintenance:
|
167
|
-
"""Maintenance tasks for the table.
|
153
|
+
return Transaction(self._tables._spiral.open_transaction(self._table, settings().file_format))
|
168
154
|
|
169
|
-
|
170
|
-
"""
|
171
|
-
return Maintenance(
|
155
|
+
def maintenance(self) -> Maintenance:
|
156
|
+
"""Access maintenance operations for a table."""
|
157
|
+
return Maintenance(self._tables._spiral.open_maintenance(self._table, settings().file_format))
|
@@ -1,5 +1,9 @@
|
|
1
|
-
from
|
2
|
-
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
|
3
|
+
from spiral.core.table import TableTransaction
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from spiral.expressions.base import ExprLike
|
3
7
|
|
4
8
|
|
5
9
|
class Transaction:
|
@@ -26,7 +30,7 @@ class Transaction:
|
|
26
30
|
else:
|
27
31
|
self._transaction.abort()
|
28
32
|
|
29
|
-
def write(self, expr: ExprLike, *, partition_size_bytes: int | None = None):
|
33
|
+
def write(self, expr: "ExprLike", *, partition_size_bytes: int | None = None):
|
30
34
|
"""Write an item to the table inside a single transaction.
|
31
35
|
|
32
36
|
:param expr: The expression to write. Must evaluate to a struct array.
|
pyspiral-0.3.1.dist-info/RECORD
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
pyspiral-0.3.1.dist-info/METADATA,sha256=QWHcYqfg46JUluQy_NZ3TKilWdvRFwOCqcJPYMo4g3c,1782
|
2
|
-
pyspiral-0.3.1.dist-info/WHEEL,sha256=WcVLymwUFXZZBOS7GhUoID5qvQcDPxnkUAYBKypRBtM,103
|
3
|
-
pyspiral-0.3.1.dist-info/entry_points.txt,sha256=uft7u-a6g40NLt4Q6BleWbK4NY0M8nZuYPpP8DV0EOk,45
|
4
|
-
spiral/catalog.py,sha256=o6b8yhVzHmk_CPNZMKyJU0H8xpjJKKcf2rjcopTSBKc,2912
|
5
|
-
spiral/scan_.py,sha256=TNAR_ELKRMJec6fIxhOwpW12WTwZc4sDLtfGdiok-lw,8239
|
6
|
-
spiral/substrait_.py,sha256=LmdRhyTX7vhgEbpxV6FUssyL-BiBEIPBeUZApeaKDM4,12671
|
7
|
-
spiral/core/core/__init__.pyi,sha256=zQhEB9X0B4EJGZf6AJYZdRt1tDQelF5tCsUaVdH7p4I,2706
|
8
|
-
spiral/core/spec/__init__.pyi,sha256=RAnvJjuQPxbcyPtjldvOU8zjVlHhnTKHAFa77sqxBdw,6714
|
9
|
-
spiral/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
spiral/core/manifests/__init__.pyi,sha256=DXr4Ab_Xo11AzrJlSj7FTSJc8qoO-SkL-_ik4kB855U,1516
|
11
|
-
spiral/core/metastore/__init__.pyi,sha256=pdKED91GVJ9XWxTWc9gwkHvVHV_RKxvL43Ofs5ndmew,3145
|
12
|
-
spiral/types_.py,sha256=W_jyO7F6rpPiH69jhgSgV7OxQZbOlb1Ho3InpKUP6Eo,155
|
13
|
-
spiral/proto/scandal/__init__.py,sha256=wAAEkPN4S4XDpGQtw1MV5zFUeHM01XSAa5tgUgcALvg,777
|
14
|
-
spiral/proto/util.py,sha256=smnvVo6nYH3FfDm9jqhNLaXz4bbTBaQezHQDCTvZyiQ,1486
|
15
|
-
spiral/proto/spiral/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
spiral/proto/spiral/table/__init__.py,sha256=_F1f52RMkZsXofPXpJb2KE8KR5l6zxCtrGrabR1uDxo,2816
|
17
|
-
spiral/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
|
-
spiral/proto/_/scandal/__init__.py,sha256=4UV2TipI1dJWCM2gFNmZM1qbM1_C_pilO3DTI1Kqq8M,8655
|
19
|
-
spiral/proto/_/arrow/flight/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
spiral/proto/_/arrow/flight/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
spiral/proto/_/arrow/flight/protocol/sql/__init__.py,sha256=_xhj9QkWEW1qZ-iVxcQ8k4EjYr7KJ5ofitJGqVUGQi4,79921
|
22
|
-
spiral/proto/_/arrow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
|
-
spiral/proto/_/spiral/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
|
-
spiral/proto/_/spiral/table/__init__.py,sha256=WnBvPPGHphSIa5K6ct6G3S3pK5BJtcC0EIce7r4Jam4,8818
|
25
|
-
spiral/proto/_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
-
spiral/proto/_/spiraldb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
|
-
spiral/proto/_/spiraldb/metastore/__init__.py,sha256=40Egtg8MRYTaTTYRKOHkwuiyXEkw3Yg7ETCQskIzpIg,16873
|
28
|
-
spiral/proto/_/spfs/__init__.py,sha256=9WtIXr7HGslKWRHHieFDo8N_qnGL4QQyLOCWEkOKRvk,1017
|
29
|
-
spiral/proto/substrait/__init__.py,sha256=pV4-T-lwAHKkfFrNYSUGY4IkbIvuKjSo_imzF7BLj_s,126526
|
30
|
-
spiral/proto/substrait/extensions/__init__.py,sha256=yD7dg0TBqn-GK_L0qeVof1GKnwSLg_kPyQSV3kcSljs,3655
|
31
|
-
spiral/maintenance.py,sha256=UFvWBLf9L0qDxJa2VdyBzxQxN4W3D9F3QHFKWp1yWZU,304
|
32
|
-
spiral/arrow.py,sha256=LBPwZcGkP4kXb42_kl5IUwWW3DO84CV3QJDwCHjG5Dg,7225
|
33
|
-
spiral/__init__.py,sha256=Dlaha3kfmzIri8A3-d69tOC2iQrK5fXlYVSI6uU4Fus,394
|
34
|
-
spiral/cli/console.py,sha256=-OP0bB_efxhWh4lZ95KRdu-SRgSUMJ47Rbi9FHv1TlY,2577
|
35
|
-
spiral/cli/org.py,sha256=ezWhoGUkJQQAwI1jKvDP8uZPNlnou_hXtRDa1us5cSE,2935
|
36
|
-
spiral/cli/token.py,sha256=dv30aa745bbS-c3tyzQUTSxGG_N0kCt8G4bip9fP_EM,968
|
37
|
-
spiral/cli/__init__.py,sha256=CoiAJ7FDgqjG_TrU-6SpP1hyZloIlEP4wcwnrF8flHM,2237
|
38
|
-
spiral/cli/types.py,sha256=4cphJs-i0vfq_CcnHxT9FpHiZdGwxME5GnOmIGB6Thw,1436
|
39
|
-
spiral/cli/workload.py,sha256=-XreFPJcX7kZvcYE3oQMeGkkYoXi25R5nuktJPg-PuY,2000
|
40
|
-
spiral/cli/admin.py,sha256=3pIs6PxDugMtdgzfRpn_HfBDv-cwAYtF0cJP2INB01A,579
|
41
|
-
spiral/cli/fs.py,sha256=rgI3QNKg0_B-3T2d0KgjcLYQNc2M6sRK95cJ_J0nth0,1523
|
42
|
-
spiral/cli/app.py,sha256=2oZfDTgj_gZ-lFMMzzJJTnvVzQhp_iedvH-FJnaaMW0,1487
|
43
|
-
spiral/cli/table.py,sha256=eh2NAk0GlfvthwRNeIbcZTsRWU3ypFx_uu9OaOLHPUo,628
|
44
|
-
spiral/cli/login.py,sha256=C7VpqVyYO2daUeIWHoelWnSGN7cju8YEuqOy12ImH4c,381
|
45
|
-
spiral/cli/printer.py,sha256=5HD3UcszFfPk-dK8U5akuvtXqMB7PMgOB1DFYMqspG8,1625
|
46
|
-
spiral/cli/__main__.py,sha256=kNaKM2xgJo7GRogf83nYldLM-RGUR6vymdGwZxywQu0,71
|
47
|
-
spiral/cli/project.py,sha256=N9ocPRtjLT5lfrPMGbX_-bUXP6o5gq3RpoGYQR3CJ74,4524
|
48
|
-
spiral/cli/state.py,sha256=1quvei8TnDTT6mDRo58P8FUfy4w16Z9sggBz7cFgllY,70
|
49
|
-
spiral/dataset.py,sha256=53s1JGM9lQkzbZes9nXnax6XtMtCUDR0TBOeTA8wC38,14019
|
50
|
-
spiral/grpc_.py,sha256=f3czdP1Mxme42Y5--a5ogYq1TTiWn-J_MlGjwJ2mWwM,1015
|
51
|
-
spiral/debug.py,sha256=t590eAUtNWwMTsSdkjVNN7J1iMqY2p4PRJ3BWR_ozho,8999
|
52
|
-
spiral/expressions/tiff.py,sha256=fQwIn0kLFBM2Y3YYIHmTgb_EIRHKT2fNc77nioDQQw4,8044
|
53
|
-
spiral/expressions/io.py,sha256=gJ2a0FKMmdxarWKENulPRwH7KDvSJTIh_OUxX306xAM,3045
|
54
|
-
spiral/expressions/__init__.py,sha256=LqvssKQTvoyX4s5UIG_HtYZbt5i5EkghkEpsE_37MWg,6118
|
55
|
-
spiral/expressions/png.py,sha256=3SjreqYcnDL4_EWMENcaGpZVoXR49KAfJiERkFSdT-w,511
|
56
|
-
spiral/expressions/list_.py,sha256=nbo4xQAuqBsQGajq_JgORaJl8_CDvOAv14zMbqmtZh4,1814
|
57
|
-
spiral/expressions/http.py,sha256=begUydWoFHEqjeLkATvI_v66Ez6_rR-OQBWO5cHbb9c,2742
|
58
|
-
spiral/expressions/refs.py,sha256=EndnGTLSA-s3hD6QOkxfUSWpw2IUm1hnKyyiPIlhZ7I,2370
|
59
|
-
spiral/expressions/udf.py,sha256=vOlrdxiVpt7vdSgiTKX_XR86YKyu02Fdwb9xlINCby4,1363
|
60
|
-
spiral/expressions/mp4.py,sha256=0qhTP4HGI0WL-nVhke-ykqEnOYcb64Aqryplgelv-BA,2437
|
61
|
-
spiral/expressions/str_.py,sha256=tY8RXW3JWvr1-bEfCZtk5FAf11wKJnXPuA9EoeJ9tA4,1265
|
62
|
-
spiral/expressions/qoi.py,sha256=FCAsoF-3ur7CMIk2Oz5Hm4qDdb901E_7_-aKT8yss6E,511
|
63
|
-
spiral/expressions/base.py,sha256=mTBwS6CwdDaV8uotjZUiKi7GHQzX2TRPpnseJJUDrR0,4776
|
64
|
-
spiral/expressions/struct.py,sha256=MuxoBP6ESpwmjzusG-_HxHGYKvQQz6AZWzvw7vNUHJM,2007
|
65
|
-
spiral/settings.py,sha256=H0ELgA1k_YENufIWscXV7gzdU8t7JOCw0AdUDVvCFH0,4704
|
66
|
-
spiral/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
67
|
-
spiral/api/filesystems.py,sha256=_JWFbggORWOAg0yKASsEXbu8Vk-hIMOiiVrwcEcm_RA,3424
|
68
|
-
spiral/api/workloads.py,sha256=4MWs2pp9AWvx6cZhgyW-ehyCRxpHc_NAQgHaoYOEBfg,1266
|
69
|
-
spiral/api/__init__.py,sha256=Wy2ZEL0balniPP7LuYLhazxI9p3ZcgZ9g_MAZLWF3Hg,6828
|
70
|
-
spiral/api/tokens.py,sha256=WaSRr_l3i81t5u2qi3kWW-fySbyKFm-PQK1ZlmoSByc,1464
|
71
|
-
spiral/api/admin.py,sha256=HJBrRJScbcdDuFhF_06E0EyE-_Y0osfYPxVoRAyEoTc,837
|
72
|
-
spiral/api/organizations.py,sha256=-PO93HTX02IxhXM6SJpAhnAXpVW1WjthFO4-AOzZAC4,2670
|
73
|
-
spiral/api/tables.py,sha256=3Kt0tPfu3jQGIIAMUSNAMRtDRGK-STRYoHHgzZ4qFNU,2532
|
74
|
-
spiral/api/projects.py,sha256=-VGlu5V3TJ3XLCGu85bPHRFiktIADnAxfLWIH8Rmxug,4986
|
75
|
-
spiral/txn.py,sha256=SIww2vAazE1nPxyAkeiN7NMvXmBK6jHhFM_uMkEUW0o,1508
|
76
|
-
spiral/table.py,sha256=HzSRNQAI9YscdfmeHlwZcIdrK2SJ9wWq-GRRCmint_M,5541
|
77
|
-
spiral/authn/modal_.py,sha256=agcnR3dYTslkH2K_a2Eis_2JWn9Ps11FVrGG_jkOdGk,472
|
78
|
-
spiral/authn/device.py,sha256=ohHSVLW3a-qLNYQGN-3kXxV_836xOe0UYBN8i63cqAQ,6796
|
79
|
-
spiral/authn/authn.py,sha256=OCGJAUfoKLiXw9xAcAnX6i6mBRlvsl6qEFcimqQOu7g,2555
|
80
|
-
spiral/authn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
|
-
spiral/authn/github_.py,sha256=K-0RUHDreINjnCDHyT9aeVDRk6WtNP7noBYEcwdz2W4,1313
|
82
|
-
spiral/adbc.py,sha256=H_bzevPy5teyZKzjczh1gQ_zPcfk5sNASiJKQvyab9E,13830
|
83
|
-
spiral/project.py,sha256=q9jHql7hz5OQzPfDfvE_hN3K9cZotD0cxkdug9EUTwM,4889
|
84
|
-
spiral/_lib.abi3.so,sha256=p285aXvfZS7XzjXERyb5tsrH0fwq9rVdZ0hX4rvbkrA,64644032
|
85
|
-
pyspiral-0.3.1.dist-info/RECORD,,
|
spiral/api/tables.py
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
from typing import Annotated
|
2
|
-
|
3
|
-
from pydantic import AfterValidator, BaseModel, StringConstraints
|
4
|
-
|
5
|
-
from . import ArrowSchema, Paged, PagedRequest, PagedResponse, ProjectId, ServiceBase
|
6
|
-
|
7
|
-
|
8
|
-
def _validate_root_uri(uri: str) -> str:
|
9
|
-
if uri.endswith("/"):
|
10
|
-
raise ValueError("Root URI must not end with a slash.")
|
11
|
-
return uri
|
12
|
-
|
13
|
-
|
14
|
-
RootUri = Annotated[str, AfterValidator(_validate_root_uri)]
|
15
|
-
DatasetName = Annotated[str, StringConstraints(max_length=128, pattern=r"^[a-zA-Z_][a-zA-Z0-9_-]+$")]
|
16
|
-
TableName = Annotated[str, StringConstraints(max_length=128, pattern=r"^[a-zA-Z_][a-zA-Z0-9_-]+$")]
|
17
|
-
|
18
|
-
|
19
|
-
class TableMetadata(BaseModel):
|
20
|
-
root_uri: RootUri
|
21
|
-
spfs_mount_id: str | None = None
|
22
|
-
|
23
|
-
key_schema: ArrowSchema
|
24
|
-
|
25
|
-
# TODO(marko): Randomize this on creation of metadata.
|
26
|
-
# Column group salt is used to compute column group IDs.
|
27
|
-
# It's used to ensure that column group IDs are unique
|
28
|
-
# across different tables, even if paths are the same.
|
29
|
-
# It's never modified.
|
30
|
-
column_group_salt: int = 0
|
31
|
-
|
32
|
-
|
33
|
-
class Table(BaseModel):
|
34
|
-
id: str
|
35
|
-
project_id: ProjectId
|
36
|
-
dataset: DatasetName
|
37
|
-
table: TableName
|
38
|
-
metadata: TableMetadata
|
39
|
-
|
40
|
-
|
41
|
-
class CreateTable:
|
42
|
-
class Request(BaseModel):
|
43
|
-
project_id: ProjectId
|
44
|
-
dataset: DatasetName
|
45
|
-
table: TableName
|
46
|
-
key_schema: ArrowSchema
|
47
|
-
root_uri: RootUri | None = None
|
48
|
-
exist_ok: bool = False
|
49
|
-
|
50
|
-
class Response(BaseModel):
|
51
|
-
table: Table
|
52
|
-
|
53
|
-
|
54
|
-
class FindTable:
|
55
|
-
class Request(BaseModel):
|
56
|
-
project_id: ProjectId
|
57
|
-
dataset: DatasetName = None
|
58
|
-
table: TableName = None
|
59
|
-
|
60
|
-
class Response(BaseModel):
|
61
|
-
table: Table | None
|
62
|
-
|
63
|
-
|
64
|
-
class GetTable:
|
65
|
-
class Request(BaseModel):
|
66
|
-
id: str
|
67
|
-
|
68
|
-
class Response(BaseModel):
|
69
|
-
table: Table
|
70
|
-
|
71
|
-
|
72
|
-
class ListTables:
|
73
|
-
class Request(PagedRequest):
|
74
|
-
project_id: ProjectId
|
75
|
-
dataset: DatasetName | None = None
|
76
|
-
|
77
|
-
class Response(PagedResponse[Table]): ...
|
78
|
-
|
79
|
-
|
80
|
-
class TableService(ServiceBase):
|
81
|
-
def create(self, req: CreateTable.Request) -> CreateTable.Response:
|
82
|
-
return self.client.post("/table/create", req, CreateTable.Response)
|
83
|
-
|
84
|
-
def find(self, req: FindTable.Request) -> FindTable.Response:
|
85
|
-
return self.client.put("/table/find", req, FindTable.Response)
|
86
|
-
|
87
|
-
def get(self, req: GetTable.Request) -> GetTable.Response:
|
88
|
-
return self.client.put(f"/table/{req.id}", GetTable.Response)
|
89
|
-
|
90
|
-
def list(self, req: ListTables.Request) -> Paged[Table]:
|
91
|
-
return self.client.paged("/table/list", req, ListTables.Response)
|
spiral/api/tokens.py
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
from pydantic import BaseModel
|
2
|
-
|
3
|
-
from . import Paged, PagedRequest, PagedResponse, ServiceBase
|
4
|
-
|
5
|
-
|
6
|
-
class Token(BaseModel):
|
7
|
-
id: str
|
8
|
-
project_id: str
|
9
|
-
on_behalf_of: str
|
10
|
-
|
11
|
-
|
12
|
-
class ExchangeToken:
|
13
|
-
class Request(BaseModel): ...
|
14
|
-
|
15
|
-
class Response(BaseModel):
|
16
|
-
token: str
|
17
|
-
|
18
|
-
|
19
|
-
class IssueToken:
|
20
|
-
class Request(BaseModel): ...
|
21
|
-
|
22
|
-
class Response(BaseModel):
|
23
|
-
token: Token
|
24
|
-
token_secret: str
|
25
|
-
|
26
|
-
|
27
|
-
class RevokeToken:
|
28
|
-
class Request(BaseModel):
|
29
|
-
token_id: str
|
30
|
-
|
31
|
-
class Response(BaseModel):
|
32
|
-
token: Token
|
33
|
-
|
34
|
-
|
35
|
-
class ListTokens:
|
36
|
-
class Request(PagedRequest):
|
37
|
-
project_id: str
|
38
|
-
on_behalf_of: str | None = None
|
39
|
-
|
40
|
-
class Response(PagedResponse[Token]): ...
|
41
|
-
|
42
|
-
|
43
|
-
class TokenService(ServiceBase):
|
44
|
-
def exchange(self) -> ExchangeToken.Response:
|
45
|
-
"""Exchange a basic / identity token to a short-lived Spiral token."""
|
46
|
-
return self.client.post("/token/exchange", ExchangeToken.Request(), ExchangeToken.Response)
|
47
|
-
|
48
|
-
def issue(self) -> IssueToken.Response:
|
49
|
-
"""Issue an API token on behalf of a principal."""
|
50
|
-
return self.client.post("/token/issue", IssueToken.Request(), IssueToken.Response)
|
51
|
-
|
52
|
-
def revoke(self, request: RevokeToken.Request) -> RevokeToken.Response:
|
53
|
-
return self.client.put("/token/revoke", request, RevokeToken.Response)
|
54
|
-
|
55
|
-
def list(self, request: ListTokens.Request) -> Paged[Token]:
|
56
|
-
return self.client.paged("/token/list", request, ListTokens.Response)
|
spiral/authn/authn.py
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
import base64
|
2
|
-
import logging
|
3
|
-
import os
|
4
|
-
|
5
|
-
from spiral.api import Authn, SpiralAPI
|
6
|
-
|
7
|
-
ENV_TOKEN_ID = "SPIRAL_TOKEN_ID"
|
8
|
-
ENV_TOKEN_SECRET = "SPIRAL_TOKEN_SECRET"
|
9
|
-
|
10
|
-
log = logging.getLogger(__name__)
|
11
|
-
|
12
|
-
|
13
|
-
class FallbackAuthn(Authn):
|
14
|
-
"""Credential provider that tries multiple providers in order."""
|
15
|
-
|
16
|
-
def __init__(self, providers: list[Authn]):
|
17
|
-
self._providers = providers
|
18
|
-
|
19
|
-
def token(self) -> str | None:
|
20
|
-
for provider in self._providers:
|
21
|
-
token = provider.token()
|
22
|
-
if token is not None:
|
23
|
-
return token
|
24
|
-
return None
|
25
|
-
|
26
|
-
|
27
|
-
class TokenAuthn(Authn):
|
28
|
-
"""Credential provider that returns a fixed token."""
|
29
|
-
|
30
|
-
def __init__(self, token: str):
|
31
|
-
self._token = token
|
32
|
-
|
33
|
-
def token(self) -> str:
|
34
|
-
return self._token
|
35
|
-
|
36
|
-
|
37
|
-
class EnvironmentAuthn(Authn):
|
38
|
-
"""Credential provider that returns a basic token from the environment.
|
39
|
-
|
40
|
-
NOTE: Returns basic token. Must be exchanged.
|
41
|
-
"""
|
42
|
-
|
43
|
-
def token(self) -> str | None:
|
44
|
-
if ENV_TOKEN_ID not in os.environ:
|
45
|
-
return None
|
46
|
-
if ENV_TOKEN_SECRET not in os.environ:
|
47
|
-
raise ValueError(f"{ENV_TOKEN_SECRET} is missing.")
|
48
|
-
|
49
|
-
token_id = os.environ[ENV_TOKEN_ID]
|
50
|
-
token_secret = os.environ[ENV_TOKEN_SECRET]
|
51
|
-
basic_token = base64.b64encode(f"{token_id}:{token_secret}".encode()).decode("utf-8")
|
52
|
-
|
53
|
-
return basic_token
|
54
|
-
|
55
|
-
|
56
|
-
class DeviceAuthProvider(Authn):
|
57
|
-
"""Auth provider that uses the device flow to authenticate a Spiral user."""
|
58
|
-
|
59
|
-
def __init__(self, device_auth):
|
60
|
-
# NOTE(ngates): device_auth: spiral.auth.device_code.DeviceAuth
|
61
|
-
# We don't type it to satisfy our import linter
|
62
|
-
self._device_auth = device_auth
|
63
|
-
|
64
|
-
def token(self) -> str | None:
|
65
|
-
# TODO(ngates): only run this if we're in a notebook, CLI, or otherwise on the user's machine.
|
66
|
-
return self._device_auth.authenticate().access_token
|
67
|
-
|
68
|
-
|
69
|
-
class TokenExchangeProvider(Authn):
|
70
|
-
"""Auth provider that exchanges a basic token for a Spiral token."""
|
71
|
-
|
72
|
-
def __init__(self, authn: Authn, base_url: str):
|
73
|
-
self._authn = authn
|
74
|
-
self._token_service = SpiralAPI(authn, base_url).token
|
75
|
-
|
76
|
-
self._sp_token = None
|
77
|
-
|
78
|
-
def token(self) -> str | None:
|
79
|
-
if self._sp_token is not None:
|
80
|
-
return self._sp_token
|
81
|
-
|
82
|
-
# Don't try to exchange if token is not discovered.
|
83
|
-
if self._authn.token() is None:
|
84
|
-
return None
|
85
|
-
|
86
|
-
log.debug("Exchanging token")
|
87
|
-
self._sp_token = self._token_service.exchange().token
|
88
|
-
|
89
|
-
return self._sp_token
|