pyspiral 0.5.0__cp310-abi3-macosx_11_0_arm64.whl → 0.6.0__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.
Files changed (86) hide show
  1. {pyspiral-0.5.0.dist-info → pyspiral-0.6.0.dist-info}/METADATA +7 -3
  2. pyspiral-0.6.0.dist-info/RECORD +99 -0
  3. {pyspiral-0.5.0.dist-info → pyspiral-0.6.0.dist-info}/WHEEL +1 -1
  4. spiral/__init__.py +10 -3
  5. spiral/_lib.abi3.so +0 -0
  6. spiral/adbc.py +6 -6
  7. spiral/api/__init__.py +8 -2
  8. spiral/api/client.py +1 -1
  9. spiral/api/key_space_indexes.py +23 -0
  10. spiral/api/projects.py +15 -0
  11. spiral/api/text_indexes.py +1 -1
  12. spiral/cli/__init__.py +15 -6
  13. spiral/cli/admin.py +2 -4
  14. spiral/cli/app.py +4 -2
  15. spiral/cli/fs.py +5 -6
  16. spiral/cli/iceberg.py +97 -0
  17. spiral/cli/key_spaces.py +68 -0
  18. spiral/cli/login.py +6 -7
  19. spiral/cli/orgs.py +7 -8
  20. spiral/cli/printer.py +3 -3
  21. spiral/cli/projects.py +5 -6
  22. spiral/cli/tables.py +131 -0
  23. spiral/cli/telemetry.py +3 -4
  24. spiral/cli/text.py +115 -0
  25. spiral/cli/types.py +3 -4
  26. spiral/cli/workloads.py +7 -8
  27. spiral/client.py +111 -8
  28. spiral/core/authn/__init__.pyi +27 -0
  29. spiral/core/client/__init__.pyi +135 -63
  30. spiral/core/table/__init__.pyi +34 -24
  31. spiral/core/table/metastore/__init__.pyi +0 -4
  32. spiral/core/table/spec/__init__.pyi +0 -2
  33. spiral/{tables/dataset.py → dataset.py} +13 -7
  34. spiral/{tables/debug → debug}/manifests.py +17 -6
  35. spiral/{tables/debug → debug}/scan.py +3 -3
  36. spiral/expressions/base.py +3 -3
  37. spiral/expressions/udf.py +1 -1
  38. spiral/{iceberg/client.py → iceberg.py} +1 -3
  39. spiral/key_space_index.py +44 -0
  40. spiral/project.py +171 -18
  41. spiral/protogen/_/arrow/flight/protocol/sql/__init__.py +1668 -1110
  42. spiral/protogen/_/google/protobuf/__init__.py +2190 -0
  43. spiral/protogen/_/message_pool.py +3 -0
  44. spiral/protogen/_/py.typed +0 -0
  45. spiral/protogen/_/scandal/__init__.py +138 -126
  46. spiral/protogen/_/spfs/__init__.py +72 -0
  47. spiral/protogen/_/spql/__init__.py +61 -0
  48. spiral/protogen/_/substrait/__init__.py +5256 -2459
  49. spiral/protogen/_/substrait/extensions/__init__.py +103 -49
  50. spiral/{tables/scan.py → scan.py} +37 -44
  51. spiral/settings.py +14 -3
  52. spiral/snapshot.py +55 -0
  53. spiral/streaming_/__init__.py +3 -0
  54. spiral/streaming_/reader.py +117 -0
  55. spiral/streaming_/stream.py +146 -0
  56. spiral/substrait_.py +9 -9
  57. spiral/table.py +257 -0
  58. spiral/text_index.py +17 -0
  59. spiral/{tables/transaction.py → transaction.py} +11 -15
  60. pyspiral-0.5.0.dist-info/RECORD +0 -103
  61. spiral/cli/iceberg/__init__.py +0 -7
  62. spiral/cli/iceberg/namespaces.py +0 -47
  63. spiral/cli/iceberg/tables.py +0 -60
  64. spiral/cli/indexes/__init__.py +0 -40
  65. spiral/cli/indexes/args.py +0 -39
  66. spiral/cli/indexes/workers.py +0 -59
  67. spiral/cli/tables/__init__.py +0 -88
  68. spiral/cli/tables/args.py +0 -42
  69. spiral/core/index/__init__.pyi +0 -7
  70. spiral/iceberg/__init__.py +0 -3
  71. spiral/indexes/__init__.py +0 -5
  72. spiral/indexes/client.py +0 -137
  73. spiral/indexes/index.py +0 -28
  74. spiral/indexes/scan.py +0 -22
  75. spiral/protogen/_/spiral/table/__init__.py +0 -22
  76. spiral/protogen/substrait/__init__.py +0 -3399
  77. spiral/protogen/substrait/extensions/__init__.py +0 -115
  78. spiral/tables/__init__.py +0 -12
  79. spiral/tables/client.py +0 -133
  80. spiral/tables/maintenance.py +0 -12
  81. spiral/tables/snapshot.py +0 -78
  82. spiral/tables/table.py +0 -145
  83. {pyspiral-0.5.0.dist-info → pyspiral-0.6.0.dist-info}/entry_points.txt +0 -0
  84. /spiral/{protogen/_/spiral → debug}/__init__.py +0 -0
  85. /spiral/{tables/debug → debug}/metrics.py +0 -0
  86. /spiral/{tables/debug → protogen/_/google}/__init__.py +0 -0
spiral/project.py CHANGED
@@ -1,16 +1,23 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, Any
2
+
3
+ import pyarrow as pa
4
+
5
+ from spiral.api.projects import KeySpaceIndexResource, TableResource, TextIndexResource
6
+ from spiral.core.table.spec import Schema
7
+ from spiral.expressions import ExprLike
8
+ from spiral.key_space_index import KeySpaceIndex
9
+ from spiral.table import Table
10
+ from spiral.text_index import TextIndex
11
+ from spiral.types_ import Uri
2
12
 
3
13
  if TYPE_CHECKING:
4
14
  from spiral.client import Spiral
5
- from spiral.iceberg import Iceberg
6
- from spiral.indexes import Indexes
7
- from spiral.tables import Tables
8
15
 
9
16
 
10
17
  class Project:
11
- def __init__(self, spiral: "Spiral", id: str, name: str | None = None):
18
+ def __init__(self, spiral: "Spiral", project_id: str, name: str | None = None):
12
19
  self._spiral = spiral
13
- self._id = id
20
+ self._id = project_id
14
21
  self._name = name
15
22
 
16
23
  def __str__(self):
@@ -27,20 +34,166 @@ class Project:
27
34
  def name(self) -> str:
28
35
  return self._name or self._id
29
36
 
30
- @property
31
- def tables(self) -> "Tables":
32
- from spiral.tables import Tables
37
+ def list_tables(self) -> list[TableResource]:
38
+ return list(self._spiral.api.project.list_tables(self._id))
33
39
 
34
- return Tables(self._spiral._api, self._spiral._core, project_id=self.id)
40
+ def list_text_indexes(self) -> list[TextIndexResource]:
41
+ return list(self._spiral.api.project.list_text_indexes(self._id))
35
42
 
36
- @property
37
- def indexes(self) -> "Indexes":
38
- from spiral.indexes.client import Indexes
43
+ def list_key_space_indexes(self) -> list[KeySpaceIndexResource]:
44
+ return list(self._spiral.api.project.list_key_space_indexes(self._id))
39
45
 
40
- return Indexes(self._spiral._api, self._spiral._core, project_id=self._id)
46
+ def table(self, identifier: str) -> Table:
47
+ """Open a table with a `dataset.table` identifier, or `table` name using the `default` dataset."""
48
+ dataset, table = self._parse_table_identifier(identifier)
41
49
 
42
- @property
43
- def iceberg(self) -> "Iceberg":
44
- from spiral.iceberg import Iceberg
50
+ res = list(self._spiral.api.project.list_tables(project_id=self._id, dataset=dataset, table=table))
51
+ if len(res) == 0:
52
+ raise ValueError(f"Table not found: {self._id}.{dataset}.{table}")
53
+ res = res[0]
54
+
55
+ return Table(
56
+ self._spiral, self._spiral._core.table(res.id), identifier=f"{res.project_id}.{res.dataset}.{res.table}"
57
+ )
58
+
59
+ def create_table(
60
+ self,
61
+ identifier: str,
62
+ *,
63
+ key_schema: pa.Schema | Any,
64
+ root_uri: Uri | None = None,
65
+ exist_ok: bool = False,
66
+ ) -> Table:
67
+ """Create a new table in the project.
68
+
69
+ Args:
70
+ identifier: The table identifier, in the form `dataset.table` or `table`.
71
+ key_schema: The schema of the table's keys.
72
+ root_uri: The root URI for the table.
73
+ exist_ok: If True, do not raise an error if the table already exists.
74
+ """
75
+ dataset, table = self._parse_table_identifier(identifier)
76
+
77
+ if not isinstance(key_schema, pa.Schema):
78
+ key_schema = pa.schema(key_schema)
79
+ key_schema = Schema.from_arrow(key_schema)
80
+
81
+ core_table = self._spiral._core.create_table(
82
+ project_id=self._id,
83
+ dataset=dataset,
84
+ table=table,
85
+ key_schema=key_schema,
86
+ root_uri=root_uri,
87
+ exist_ok=exist_ok,
88
+ )
89
+
90
+ return Table(self._spiral, core_table, identifier=f"{self._id}.{dataset}.{table}")
91
+
92
+ def _parse_table_identifier(self, identifier: str) -> tuple[str, str]:
93
+ parts = identifier.split(".")
94
+ if len(parts) == 1:
95
+ return "default", parts[0]
96
+ elif len(parts) == 2:
97
+ return parts[0], parts[1]
98
+ else:
99
+ raise ValueError(f"Invalid table identifier: {self._id}.{identifier}")
100
+
101
+ def text_index(self, name: str) -> TextIndex:
102
+ """Returns the index with the given name."""
103
+ res = list(self._spiral.api.project.list_text_indexes(project_id=self._id, name=name))
104
+ if len(res) == 0:
105
+ raise ValueError(f"Index not found: {name}")
106
+ res = res[0]
107
+
108
+ return TextIndex(self._spiral._core.text_index(res.id), name=name)
109
+
110
+ def create_text_index(
111
+ self,
112
+ name: str,
113
+ *projections: ExprLike,
114
+ where: ExprLike | None = None,
115
+ root_uri: Uri | None = None,
116
+ exist_ok: bool = False,
117
+ ) -> TextIndex:
118
+ """Creates a text index over the table projection.
119
+
120
+ See `se.text.field` for how to create and configure indexable fields.
121
+
122
+ Args:
123
+ name: The index name. Must be unique within the project.
124
+ projections: At least one projection expression is required.
125
+ All projections must reference the same table.
126
+ where: An optional filter expression to apply to the index.
127
+ root_uri: The root URI for the index.
128
+ exist_ok: If True, do not raise an error if the index already exists.
129
+ """
130
+ from spiral import expressions as se
131
+
132
+ if not projections:
133
+ raise ValueError("At least one projection is required.")
134
+ projection = se.merge(*projections)
135
+ if where is not None:
136
+ where = se.lift(where)
137
+
138
+ core_index = self._spiral._core.create_text_index(
139
+ project_id=self._id,
140
+ name=name,
141
+ projection=projection.__expr__,
142
+ filter=where.__expr__ if where else None,
143
+ root_uri=root_uri,
144
+ # TODO(marko): Validate that if an index exists, it's the same?
145
+ exist_ok=exist_ok,
146
+ )
147
+
148
+ return TextIndex(core_index, name=name)
149
+
150
+ def key_space_index(self, name: str) -> KeySpaceIndex:
151
+ """Returns the index with the given name."""
152
+ res = list(self._spiral.api.project.list_key_space_indexes(project_id=self._id, name=name))
153
+ if len(res) == 0:
154
+ raise ValueError(f"Index not found: {name}")
155
+ res = res[0]
156
+
157
+ return KeySpaceIndex(self._spiral._core.key_space_index(res.id), name=name)
158
+
159
+ def create_key_space_index(
160
+ self,
161
+ name: str,
162
+ granularity: int,
163
+ *projections: ExprLike,
164
+ where: ExprLike | None = None,
165
+ root_uri: Uri | None = None,
166
+ exist_ok: bool = False,
167
+ ) -> KeySpaceIndex:
168
+ """Creates a key space index over the table projection.
169
+
170
+ Args:
171
+ name: The index name. Must be unique within the project.
172
+ granularity: The granularity at which to store keys, i.e. the size of desired key ranges.
173
+ The key ranges will not be greater than 2x the granularity, but may be smaller.
174
+ projections: At least one projection expression is required.
175
+ All projections must reference the same table.
176
+ where: An optional filter expression to apply to the index.
177
+ root_uri: The root URI for the index.
178
+ exist_ok: If True, do not raise an error if the index already exists.
179
+ """
180
+ from spiral import expressions as se
181
+
182
+ if not projections:
183
+ raise ValueError("At least one projection is required.")
184
+ projection = se.merge(*projections)
185
+ if where is not None:
186
+ where = se.lift(where)
187
+
188
+ core_index = self._spiral._core.create_key_space_index(
189
+ project_id=self._id,
190
+ name=name,
191
+ granularity=granularity,
192
+ projection=projection.__expr__,
193
+ filter=where.__expr__ if where else None,
194
+ root_uri=root_uri,
195
+ # TODO(marko): Validate that if an index exists, it's the same?
196
+ exist_ok=exist_ok,
197
+ )
45
198
 
46
- return Iceberg(self._spiral, project_id=self._id)
199
+ return KeySpaceIndex(core_index, name=name)