pyspiral 0.5.0__cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 0.6.1__cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.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.1.dist-info}/METADATA +7 -3
  2. pyspiral-0.6.1.dist-info/RECORD +99 -0
  3. {pyspiral-0.5.0.dist-info → pyspiral-0.6.1.dist-info}/WHEEL +1 -1
  4. spiral/__init__.py +11 -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 +89 -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 +152 -63
  30. spiral/core/table/__init__.pyi +17 -27
  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 +15 -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} +38 -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 +131 -0
  55. spiral/streaming_/stream.py +146 -0
  56. spiral/substrait_.py +9 -9
  57. spiral/table.py +259 -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.1.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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyspiral
3
- Version: 0.5.0
3
+ Version: 0.6.1
4
4
  Classifier: Intended Audience :: Science/Research
5
5
  Classifier: Operating System :: OS Independent
6
6
  Classifier: Programming Language :: Python
@@ -12,8 +12,7 @@ Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Classifier: Programming Language :: Rust
14
14
  Classifier: License :: Other/Proprietary License
15
- Requires-Dist: betterproto==2.0.0b7
16
- Requires-Dist: datasets>=4.0.0
15
+ Requires-Dist: betterproto2>=0.8.0
17
16
  Requires-Dist: google-re2>=1.1.20240702
18
17
  Requires-Dist: grpclib>=0.4.7
19
18
  Requires-Dist: hishel>=0.0.30
@@ -32,10 +31,15 @@ Requires-Dist: typer>=0.16
32
31
  Requires-Dist: xxhash>=3.4.1
33
32
  Requires-Dist: polars>=1.31.0 ; extra == 'polars'
34
33
  Requires-Dist: duckdb>=1.3.2 ; extra == 'duckdb'
34
+ Requires-Dist: datasets>=4.0.0 ; extra == 'datasets'
35
35
  Requires-Dist: pyiceberg>=0.9.1 ; extra == 'pyiceberg'
36
+ Requires-Dist: mosaicml-streaming>=0.13.0 ; extra == 'streaming'
37
+ Requires-Dist: vortex-data>=0.52.1 ; extra == 'streaming'
36
38
  Provides-Extra: polars
37
39
  Provides-Extra: duckdb
40
+ Provides-Extra: datasets
38
41
  Provides-Extra: pyiceberg
42
+ Provides-Extra: streaming
39
43
  Summary: Python client for Spiral.
40
44
  Home-Page: https://spiraldb.com
41
45
  Author-email: SpiralDB <hello@spiraldb.com>
@@ -0,0 +1,99 @@
1
+ pyspiral-0.6.1.dist-info/METADATA,sha256=dvU4zmSKjosVkzzsl8kYTMvaVneaplCKGJ0ml4SBRxo,1836
2
+ pyspiral-0.6.1.dist-info/WHEEL,sha256=PxcKzGLVtZeSnGJDErQ-Emkn2AvBXbmzIogfnaf7-q8,130
3
+ pyspiral-0.6.1.dist-info/entry_points.txt,sha256=uft7u-a6g40NLt4Q6BleWbK4NY0M8nZuYPpP8DV0EOk,45
4
+ spiral/__init__.py,sha256=5c0faqg-kHZBDwriQ7LzLAMcFolIucp-IA1EzNvCZ3k,711
5
+ spiral/_lib.abi3.so,sha256=2-DAyExXsya0cbr_NZlY2HaguPy17G7wFTfp3BS8YFE,54826728
6
+ spiral/adbc.py,sha256=7IxfWIeQN-fh0W5OdN_PP2x3pzQYg6ZUOLsHg3jktqw,14842
7
+ spiral/api/__init__.py,sha256=ULBlVq3PnfNOO6T5naE_ULmmii-83--qTuN2PpAUQN0,2241
8
+ spiral/api/admin.py,sha256=A1iVR1XYJSObZivPAD5UzmPuMgupXc9kaHNYYa_kwfs,585
9
+ spiral/api/client.py,sha256=c63u4Nv0XqXW3BpGAofMk44d-1_4RymKwbcMzq9qxeY,4649
10
+ spiral/api/filesystems.py,sha256=EA4iqhTeaIlvObvEUxHmZl0pQ24IOxUVWM3GPhFLw8o,4969
11
+ spiral/api/key_space_indexes.py,sha256=-38rZXTdkL4mLhp9h3CtqyIyutzzq88tV6bhK05MqYE,640
12
+ spiral/api/organizations.py,sha256=B-8zZ7lFJANGK7dUNbo_aU-cgI959JBP9VcWb6wdgi0,1895
13
+ spiral/api/projects.py,sha256=62Y1lqI_TpUh3WKQqrjbLWJHiZsI_X3g8u2RTbUwkoA,6162
14
+ spiral/api/telemetry.py,sha256=tfdA3E_EWJwFVxkQfkm8tiYGRubnx2LuE5nbfsk1oG4,474
15
+ spiral/api/text_indexes.py,sha256=_zVlGBytl-9-Unbu2POfZgLh40H1YRcagFtplgIG428,1828
16
+ spiral/api/types.py,sha256=lGdiKViRgIEJXD2ubwnyEIEwHkfRumlZjVEaHMV3Tm8,682
17
+ spiral/api/workers.py,sha256=0wZNUHMioDT53P1OBJfpjyDfIodHwwT6858z2IlRIM4,636
18
+ spiral/api/workloads.py,sha256=XAyXV7vgZcoyyoPoGvOT4jTpyFKFMvrrAfhL6d1h1kE,1748
19
+ spiral/arrow_.py,sha256=T1LZ7bh9aMDbXfpUsf0dR0E1roTQyAYSgZ2mL4s8J_4,7681
20
+ spiral/cli/__init__.py,sha256=LutjpWZu5Rvmba8C8bPa5vOCv74JuAoE1kvz0nd48dE,2476
21
+ spiral/cli/__main__.py,sha256=kNaKM2xgJo7GRogf83nYldLM-RGUR6vymdGwZxywQu0,71
22
+ spiral/cli/admin.py,sha256=-ubYqs8nKjnQStbQ68jpWx_9xh0TsaxI0wM1Hfko8_U,319
23
+ spiral/cli/app.py,sha256=HWCjMJLzSz_JaiLF046jzC9A4-yvzS6506D3cOR2Vgc,1773
24
+ spiral/cli/console.py,sha256=6JHbAQV6MFWz3P-VzqPOjhHpkIQagsCdzTMvmuDKMkU,2580
25
+ spiral/cli/fs.py,sha256=UREIJhjr6MfIdcKK7pjUKICd0wsQULhQiWRVWUnQ0dc,4376
26
+ spiral/cli/iceberg.py,sha256=Q14tcGcn1LixbFCYP0GhfYwFFXTmmi8tqBPYwalJEyE,3248
27
+ spiral/cli/key_spaces.py,sha256=x3IFRP5d47pKiAHeWExYMOBaT2TwxbWjVM01SUqKrwI,2943
28
+ spiral/cli/login.py,sha256=TgTr37ImgG1NKN8VbtqkxVAYaZFpMXMwPAb23gVldEw,649
29
+ spiral/cli/orgs.py,sha256=fmOuLxpeIFfKqePRi292Gv9k-EF5pPn_tbKd2BLl2Ig,2869
30
+ spiral/cli/printer.py,sha256=HcvSUpaMItzmhBUfIHROK1Z3SL8J8wDopS3Qo8H00uw,1781
31
+ spiral/cli/projects.py,sha256=UYrBlLcFacuXExdLX1sZByfvkz9MRtk_0oRAZvqHa0w,5105
32
+ spiral/cli/state.py,sha256=10wTIVQ0SJkY67Z6-KQ1LFlt3aVIPmZhoHFdTwp4kNA,130
33
+ spiral/cli/tables.py,sha256=48lZ0wPQSCTul1vn-Qx6Be5eGnw75Abtw2zxMK9dCPg,4613
34
+ spiral/cli/telemetry.py,sha256=Uxo1Q1FkKJ6n6QNGOUmL3j_pRRWRx0qWIhoP-U9BuR0,589
35
+ spiral/cli/text.py,sha256=DlWGe4JrkdERAiqyITNpk91Wqb63Re99rNYlIFsIamc,4031
36
+ spiral/cli/types.py,sha256=XYzo1GgX7dBBItoBSrHI4vO5C2lLmS2sktb-2GnGH3E,1362
37
+ spiral/cli/workloads.py,sha256=2_SLfQTFN6y73R9H0i9dk8VIOVagKxSxOpHXC56yptY,2015
38
+ spiral/client.py,sha256=Po9xgCH3NwVsCeRZMm3eJUPV77Rknyj-9MfCS1TbdTg,6623
39
+ spiral/core/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ spiral/core/authn/__init__.pyi,sha256=Jw_8ywTMDTwgAtGxMtFED63rU0jOgrv-eZtaZ5sR5t4,757
41
+ spiral/core/client/__init__.pyi,sha256=iEhZgbySG5LScfrtkiiHW1iHghgehsrVmPP-v5Pv_vk,5740
42
+ spiral/core/table/__init__.pyi,sha256=_N9JyGoJXcbJWXjZal0HSSxNbbMptrqKZJHj5GA_mQg,2974
43
+ spiral/core/table/manifests/__init__.pyi,sha256=3V59-K1qr1z2dGfgRKXaHSVheK8NNw8Q8PFhfbeQd_4,1065
44
+ spiral/core/table/metastore/__init__.pyi,sha256=rc3u9MwEKRvL2kxOc8lBorddFRnM8o_o1frqtae86a4,1697
45
+ spiral/core/table/spec/__init__.pyi,sha256=0NyGeyEhV_ebwKWVU3sqSvdF2D9v8kEVwo6wYAHF99M,5579
46
+ spiral/dataset.py,sha256=NNqG-oOrhbmNC2OMZ9AYAm4YkwwBozeRI6zXtz4cspA,8008
47
+ spiral/datetime_.py,sha256=1TA1RYIRU22qcUuipIjVhAtGnPDVn2z9WttuhkmfkwY,964
48
+ spiral/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
+ spiral/debug/manifests.py,sha256=oaPB4534pQdqvPXCZetVNSvvhpdXTrv_1pN-_bAkeAo,2893
50
+ spiral/debug/metrics.py,sha256=XdRDcjggtsLNGCAjam6IxG9072pz_d2C8iLApNRFUtk,2044
51
+ spiral/debug/scan.py,sha256=9bMmVQFs5M6Rldm0fmrmmvn9LbSSTKBV5tIu37mEn78,8938
52
+ spiral/expressions/__init__.py,sha256=T8PIb0_UB9kynK0dpWbUD4No5lKRTG-wKnao8xOcXjY,6381
53
+ spiral/expressions/base.py,sha256=OOUDrbkLBE0lSkAmM-6FP2F2N8zhN_in3S_UDrWLDeQ,4805
54
+ spiral/expressions/http.py,sha256=begUydWoFHEqjeLkATvI_v66Ez6_rR-OQBWO5cHbb9c,2742
55
+ spiral/expressions/io.py,sha256=gJ2a0FKMmdxarWKENulPRwH7KDvSJTIh_OUxX306xAM,3045
56
+ spiral/expressions/list_.py,sha256=MMt5lf5H1M3O-x6N_PvqOLGq9NOk6Ukv0fPWwPC_uy4,1809
57
+ spiral/expressions/mp4.py,sha256=_xGVnkygddzxP9a8OACJ8_KXnejuVbYCVKBCXBQ798Y,2151
58
+ spiral/expressions/png.py,sha256=KO8X0OmMzUFwpg2I_j0JTyldPzVXDWIMzjWMWDV9vIY,506
59
+ spiral/expressions/qoi.py,sha256=gvIbb6fXb_Bb080sn9wkpbGGrPs2UEcTXCfuv4-kcYQ,506
60
+ spiral/expressions/refs.py,sha256=omeHBQ5o6N4xgZ3x5Xz7IRrWwYBBtQY8DYK0NNAxeGo,2109
61
+ spiral/expressions/str_.py,sha256=tY8RXW3JWvr1-bEfCZtk5FAf11wKJnXPuA9EoeJ9tA4,1265
62
+ spiral/expressions/struct.py,sha256=pGAnCDh6AK0BK1XfZ1qG4ce4ranIQEE1HQsgmzBcfwQ,2038
63
+ spiral/expressions/text.py,sha256=-02gBWYoyNQ3qQ1--9HTa8IryUDojYQVIp8C7rgnOWQ,1893
64
+ spiral/expressions/tiff.py,sha256=fQwIn0kLFBM2Y3YYIHmTgb_EIRHKT2fNc77nioDQQw4,8044
65
+ spiral/expressions/udf.py,sha256=yb9MIcrFftpNDxgBF228cvdv6TY-hEFikYz2fq_nzWo,1353
66
+ spiral/grpc_.py,sha256=f3czdP1Mxme42Y5--a5ogYq1TTiWn-J_MlGjwJ2mWwM,1015
67
+ spiral/iceberg.py,sha256=JGq62Qnf296r9_hRAoH85GQq45-uSBjwXWw_CvPi6G4,930
68
+ spiral/key_space_index.py,sha256=NAB_nONEjpMYbse8suz42w7Qb5OPHuKN9h9CT2NJe08,1460
69
+ spiral/project.py,sha256=CO_Pn6vPqaonNvRdCNRFcBWr4TqO2AsAUTH5xawIeCE,7283
70
+ spiral/protogen/_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ spiral/protogen/_/arrow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
+ spiral/protogen/_/arrow/flight/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ spiral/protogen/_/arrow/flight/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ spiral/protogen/_/arrow/flight/protocol/sql/__init__.py,sha256=yt4_UDWfOaVpyCBeQa2aVXIfZzRSrcfIQHsXFCWv0qI,90023
75
+ spiral/protogen/_/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
+ spiral/protogen/_/google/protobuf/__init__.py,sha256=lsfhHEPczGOxrOmkstAqh64V0Kt8hQE_6N0tIpc27HU,75116
77
+ spiral/protogen/_/message_pool.py,sha256=4-cRhhiM6bmfpUJZ8qxc8LEyqHBHpLCcotjbyZxl7JM,71
78
+ spiral/protogen/_/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
+ spiral/protogen/_/scandal/__init__.py,sha256=-4m9DHPjtLFzpnesaAv8W_p8R_kfGjA5z3l0GPPbjD8,4965
80
+ spiral/protogen/_/spfs/__init__.py,sha256=4lnc88HhuH4t-JR9NjXz5r5WVESxCEbyUpV7Xfc-SBI,2028
81
+ spiral/protogen/_/spql/__init__.py,sha256=JJBlNacSIIoo5cazHFyLtdkGRLYgwNru1FstFpuPGg8,1548
82
+ spiral/protogen/_/substrait/__init__.py,sha256=-PqWiWMN0hl3Gntj5l1wpEhOMdGDgv3PskGPouaRct8,209839
83
+ spiral/protogen/_/substrait/extensions/__init__.py,sha256=sCMvwWCXWu2cSGiTEH0hRjkn0WTsePLDIxRBBNpENJs,5326
84
+ spiral/protogen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ spiral/protogen/util.py,sha256=smnvVo6nYH3FfDm9jqhNLaXz4bbTBaQezHQDCTvZyiQ,1486
86
+ spiral/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
+ spiral/scan.py,sha256=20-NSGsoXYf6uKQ7yEdbbwT8ijIK7KxKTctycsl0AIk,7073
88
+ spiral/server.py,sha256=ztBmB5lBnUz-smQxR_tC8AI5SOhz17wH0MI3GuzDUdM,600
89
+ spiral/settings.py,sha256=Nap68xM-1ZvF3yDhkyRnNDIAVMIgxmIksglg_1iT0-0,3069
90
+ spiral/snapshot.py,sha256=_l2wrqUXz2RARjIDxOWw4aQpegJohvggIoWuCllzStA,1506
91
+ spiral/streaming_/__init__.py,sha256=s7MlW2ERsuZmZGExLFL6RcZon2e0tNBocBg5ANgki7k,61
92
+ spiral/streaming_/reader.py,sha256=Kpqknv2jn12jUhHOEEDArj0JZwrWb8XjoOGs9HrdVyA,4047
93
+ spiral/streaming_/stream.py,sha256=xFTtGB6CspEKstzBeyyaOeOR3KDiJc21m07ZpD1AXZQ,5669
94
+ spiral/substrait_.py,sha256=AKeOD4KIXvz2J4TYxnIneOiHddtBIyOhuNxVO_uH0eg,12592
95
+ spiral/table.py,sha256=knJjSCPrFLXmjCbUzslGZ7avH0V_Z0o-rsctWDzmGK4,9821
96
+ spiral/text_index.py,sha256=FQ9rgIEGLSJryS9lFdMhKtPFey18BXoWbPXyvZPJJ04,442
97
+ spiral/transaction.py,sha256=O3vSaTc7zpeC5qbqnj-VWKwK6rrp_mYV2JuPHp2ZJ80,1464
98
+ spiral/types_.py,sha256=W_jyO7F6rpPiH69jhgSgV7OxQZbOlb1Ho3InpKUP6Eo,155
99
+ pyspiral-0.6.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.9.3)
2
+ Generator: maturin (1.9.4)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64
spiral/__init__.py CHANGED
@@ -3,8 +3,16 @@
3
3
  # This is here to make sure we load the native extension first
4
4
  from spiral import _lib
5
5
 
6
- assert _lib
6
+ # Eagerly import the Spiral library
7
+ assert _lib, "Spiral library"
7
8
 
8
- from spiral.client import Spiral # noqa: E402, I001
9
+ from spiral.client import Spiral # noqa: E402
10
+ from spiral.core.client import ShuffleStrategy # noqa: E402
11
+ from spiral.key_space_index import KeySpaceIndex # noqa: E402
12
+ from spiral.project import Project # noqa: E402
13
+ from spiral.scan import Scan # noqa: E402
14
+ from spiral.snapshot import Snapshot # noqa: E402
15
+ from spiral.table import Table # noqa: E402
16
+ from spiral.text_index import TextIndex # noqa: E402
9
17
 
10
- __all__ = ["Spiral"]
18
+ __all__ = ["Spiral", "Project", "Table", "Snapshot", "Scan", "ShuffleStrategy", "TextIndex", "KeySpaceIndex"]
spiral/_lib.abi3.so CHANGED
Binary file
spiral/adbc.py CHANGED
@@ -8,7 +8,6 @@ import pyarrow as pa
8
8
  import pyarrow.compute as pc
9
9
  import sqlglot
10
10
  import sqlglot.expressions as exp
11
- from betterproto.lib.google.protobuf import Any
12
11
  from pyarrow.flight import (
13
12
  Action,
14
13
  FlightDescriptor,
@@ -35,7 +34,8 @@ from spiral.protogen._.arrow.flight.protocol.sql import (
35
34
  SqlInfo,
36
35
  SqlSupportedTransaction,
37
36
  )
38
- from spiral.tables import Snapshot
37
+ from spiral.protogen._.google.protobuf import Any
38
+ from spiral.snapshot import Snapshot
39
39
 
40
40
  log = logging.getLogger(__name__)
41
41
  logging.getLogger("sqlx").setLevel(logging.WARNING)
@@ -152,7 +152,7 @@ class SpiralADBCServer(ADBCServerBase):
152
152
  dataset = tbl.db or "default"
153
153
  table = tbl.name
154
154
 
155
- return self.sp.project(project).tables.table(f"{dataset}.{table}").snapshot()
155
+ return self.sp.project(project).table(f"{dataset}.{table}").snapshot()
156
156
 
157
157
  def get_catalogs(self, req: CommandGetCatalogs) -> pa.RecordBatchReader:
158
158
  schema = pa.schema([pa.field("catalog_name", pa.string(), nullable=False)])
@@ -190,7 +190,7 @@ class SpiralADBCServer(ADBCServerBase):
190
190
  projects = [self.sp.project(req.catalog)]
191
191
 
192
192
  for project in projects:
193
- datasets = {tbl.dataset for tbl in project.tables.list_tables()}
193
+ datasets = {tbl.dataset for tbl in project.list_tables()}
194
194
 
195
195
  batch = pa.RecordBatch.from_arrays(
196
196
  [
@@ -234,7 +234,7 @@ class SpiralADBCServer(ADBCServerBase):
234
234
  projects = sorted(projects, key=lambda p: p.id)
235
235
 
236
236
  def _process_project(project):
237
- tables: list[TableResource] = project.tables.list_tables()
237
+ tables: list[TableResource] = project.list_tables()
238
238
 
239
239
  rows = []
240
240
  for table in tables:
@@ -246,7 +246,7 @@ class SpiralADBCServer(ADBCServerBase):
246
246
  }
247
247
 
248
248
  if req.include_schema:
249
- open_table = project.tables.table(f"{table.project_id}.{table.dataset}.{table.table}")
249
+ open_table = project.table(f"{table.dataset}.{table.table}")
250
250
  row["table_schema"] = open_table.snapshot().to_dataset().schema.serialize().to_pybytes()
251
251
 
252
252
  rows.append(row)
spiral/api/__init__.py CHANGED
@@ -3,8 +3,6 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  import httpx
5
5
 
6
- from spiral.api.text_indexes import TextIndexesService
7
-
8
6
  from .client import _Client
9
7
 
10
8
  if TYPE_CHECKING:
@@ -12,9 +10,11 @@ if TYPE_CHECKING:
12
10
 
13
11
  from .admin import AdminService
14
12
  from .filesystems import FileSystemService
13
+ from .key_space_indexes import KeySpaceIndexesService
15
14
  from .organizations import OrganizationService
16
15
  from .projects import ProjectService
17
16
  from .telemetry import TelemetryService
17
+ from .text_indexes import TextIndexesService
18
18
  from .workloads import WorkloadService
19
19
 
20
20
 
@@ -65,6 +65,12 @@ class SpiralAPI:
65
65
 
66
66
  return TextIndexesService(self.client)
67
67
 
68
+ @property
69
+ def key_space_indexes(self) -> "KeySpaceIndexesService":
70
+ from .key_space_indexes import KeySpaceIndexesService
71
+
72
+ return KeySpaceIndexesService(self.client)
73
+
68
74
  @property
69
75
  def telemetry(self) -> "TelemetryService":
70
76
  from .telemetry import TelemetryService
spiral/api/client.py CHANGED
@@ -6,7 +6,7 @@ import httpx
6
6
  from httpx import HTTPStatusError
7
7
  from pydantic import BaseModel, Field, TypeAdapter
8
8
 
9
- from spiral.core.client import Authn
9
+ from spiral.core.authn import Authn
10
10
 
11
11
  log = logging.getLogger(__name__)
12
12
 
@@ -0,0 +1,23 @@
1
+ from pydantic import BaseModel
2
+
3
+ from .client import ServiceBase
4
+ from .types import IndexId, WorkerId
5
+ from .workers import ResourceClass
6
+
7
+
8
+ class SyncIndexRequest(BaseModel):
9
+ """Request to sync a text index."""
10
+
11
+ resources: ResourceClass
12
+
13
+
14
+ class SyncIndexResponse(BaseModel):
15
+ worker_id: WorkerId
16
+
17
+
18
+ class KeySpaceIndexesService(ServiceBase):
19
+ """Service for key space index operations."""
20
+
21
+ def sync_index(self, index_id: IndexId, request: SyncIndexRequest) -> SyncIndexResponse:
22
+ """Start a job to sync an index."""
23
+ return self.client.post(f"/v1/key-space-indexes/{index_id}/sync", request, SyncIndexResponse)
spiral/api/projects.py CHANGED
@@ -138,6 +138,12 @@ class TextIndexResource(BaseModel):
138
138
  name: str
139
139
 
140
140
 
141
+ class KeySpaceIndexResource(BaseModel):
142
+ id: str
143
+ project_id: ProjectId
144
+ name: str
145
+
146
+
141
147
  class ProjectService(ServiceBase):
142
148
  """Service for project operations."""
143
149
 
@@ -169,6 +175,15 @@ class ProjectService(ServiceBase):
169
175
  f"/v1/projects/{project_id}/text-indexes", PagedResponse[TextIndexResource], params=params
170
176
  )
171
177
 
178
+ def list_key_space_indexes(self, project_id: ProjectId, name: str | None = None) -> Paged[KeySpaceIndexResource]:
179
+ """List key space indexes in a project."""
180
+ params = {}
181
+ if name:
182
+ params["name"] = name
183
+ return self.client.paged(
184
+ f"/v1/projects/{project_id}/key-space-indexes", PagedResponse[KeySpaceIndexResource], params=params
185
+ )
186
+
172
187
  def get(self, project_id: ProjectId) -> Project:
173
188
  """Get a project."""
174
189
  return self.client.get(f"/v1/projects/{project_id}", Project)
@@ -33,7 +33,7 @@ class SyncIndexResponse(BaseModel):
33
33
 
34
34
 
35
35
  class TextIndexesService(ServiceBase):
36
- """Service for workload operations."""
36
+ """Service for text index operations."""
37
37
 
38
38
  def create_worker(self, index_id: IndexId, request: CreateWorkerRequest) -> CreateWorkerResponse:
39
39
  """Create a new search worker."""
spiral/cli/__init__.py CHANGED
@@ -1,16 +1,23 @@
1
1
  import asyncio
2
2
  import functools
3
3
  import inspect
4
- from typing import IO
4
+ from collections.abc import Callable
5
+ from typing import IO, Generic, ParamSpec, TypeVar, override
5
6
 
6
- import rich
7
7
  import typer
8
8
  from click import ClickException
9
9
  from grpclib import GRPCError
10
10
  from httpx import HTTPStatusError
11
+ from rich.console import Console
11
12
 
13
+ P = ParamSpec("P")
14
+ T = TypeVar("T")
12
15
 
13
- class AsyncTyper(typer.Typer):
16
+ CONSOLE = Console()
17
+ ERR_CONSOLE = Console(stderr=True, style="red")
18
+
19
+
20
+ class AsyncTyper(typer.Typer, Generic[P]):
14
21
  """Wrapper to allow async functions to be used as commands.
15
22
 
16
23
  We also pre-bake some configuration.
@@ -25,13 +32,15 @@ class AsyncTyper(typer.Typer):
25
32
  **kwargs,
26
33
  )
27
34
 
28
- def callback(self, *args, **kwargs):
35
+ @override
36
+ def callback(self, *args, **kwargs) -> Callable[[Callable[P, T]], Callable[P, T]]:
29
37
  decorator = super().callback(*args, **kwargs)
30
38
  for wrapper in (_wrap_exceptions, _maybe_run_async):
31
39
  decorator = functools.partial(wrapper, decorator)
32
40
  return decorator
33
41
 
34
- def command(self, *args, **kwargs):
42
+ @override
43
+ def command(self, *args, **kwargs) -> Callable[[Callable[P, T]], Callable[P, T]]:
35
44
  decorator = super().command(*args, **kwargs)
36
45
  for wrapper in (_wrap_exceptions, _maybe_run_async):
37
46
  decorator = functools.partial(wrapper, decorator)
@@ -50,7 +59,7 @@ class _ClickGRPCException(ClickException):
50
59
  return self.message
51
60
 
52
61
  def show(self, file: IO[str] | None = None) -> None:
53
- rich.print(f"Error: {self.format_message()}", file=file)
62
+ ERR_CONSOLE.print(f"Error: {self.format_message()}")
54
63
 
55
64
 
56
65
  def _maybe_run_async(decorator, f):
spiral/cli/admin.py CHANGED
@@ -1,7 +1,5 @@
1
- from rich import print
2
-
3
1
  from spiral.api.types import OrgId
4
- from spiral.cli import AsyncTyper, state
2
+ from spiral.cli import CONSOLE, AsyncTyper, state
5
3
 
6
4
  app = AsyncTyper()
7
5
 
@@ -13,4 +11,4 @@ def sync(
13
11
  state.settings.api._admin.sync_orgs()
14
12
 
15
13
  for membership in state.settings.api._admin.sync_memberships(org_id):
16
- print(membership)
14
+ CONSOLE.print(membership)
spiral/cli/app.py CHANGED
@@ -8,13 +8,14 @@ from spiral.cli import (
8
8
  console,
9
9
  fs,
10
10
  iceberg,
11
- indexes,
11
+ key_spaces,
12
12
  login,
13
13
  orgs,
14
14
  projects,
15
15
  state,
16
16
  tables,
17
17
  telemetry,
18
+ text,
18
19
  workloads,
19
20
  )
20
21
  from spiral.settings import LOG_DIR, Settings
@@ -36,7 +37,8 @@ app.add_typer(orgs.app, name="orgs")
36
37
  app.add_typer(projects.app, name="projects")
37
38
  app.add_typer(iceberg.app, name="iceberg")
38
39
  app.add_typer(tables.app, name="tables")
39
- app.add_typer(indexes.app, name="indexes")
40
+ app.add_typer(key_spaces.app, name="ks")
41
+ app.add_typer(text.app, name="text")
40
42
  app.add_typer(telemetry.app, name="telemetry")
41
43
  app.command("console")(console.command)
42
44
  app.command("login")(login.command)
spiral/cli/fs.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from typing import Annotated
2
2
 
3
3
  import questionary
4
- import rich
5
4
  from pydantic import SecretStr
6
5
  from typer import Option
7
6
 
@@ -13,7 +12,7 @@ from spiral.api.filesystems import (
13
12
  UpdateS3FileSystem,
14
13
  UpstreamFileSystem,
15
14
  )
16
- from spiral.cli import AsyncTyper, state
15
+ from spiral.cli import CONSOLE, AsyncTyper, state
17
16
  from spiral.cli.types import ProjectArg, ask_project
18
17
 
19
18
  app = AsyncTyper(short_help="File Systems.")
@@ -24,9 +23,9 @@ def show(project: ProjectArg):
24
23
  file_system = state.settings.api.file_system.get_file_system(project)
25
24
  match file_system:
26
25
  case BuiltinFileSystem(provider=provider):
27
- rich.print(f"provider: {provider}")
26
+ CONSOLE.print(f"provider: {provider}")
28
27
  case _:
29
- rich.print(file_system)
28
+ CONSOLE.print(file_system)
30
29
 
31
30
 
32
31
  def ask_provider():
@@ -103,10 +102,10 @@ def update(
103
102
  raise ValueError("Must specify either --s3 or --gcs.")
104
103
 
105
104
  res = state.settings.api.file_system.update_file_system(project, file_system)
106
- rich.print(res.file_system)
105
+ CONSOLE.print(res.file_system)
107
106
 
108
107
 
109
108
  @app.command(help="Lists the available built-in file system providers.")
110
109
  def list_providers():
111
110
  for provider in state.settings.api.file_system.list_providers():
112
- rich.print(provider)
111
+ CONSOLE.print(provider)
spiral/cli/iceberg.py ADDED
@@ -0,0 +1,97 @@
1
+ import sys
2
+ from typing import Annotated
3
+
4
+ import rich
5
+ import typer
6
+ from typer import Argument
7
+
8
+ from spiral.cli import CONSOLE, ERR_CONSOLE, AsyncTyper, state
9
+ from spiral.cli.types import ProjectArg
10
+
11
+ app = AsyncTyper(short_help="Apache Iceberg Catalog")
12
+
13
+
14
+ @app.command(help="List namespaces.")
15
+ def namespaces(
16
+ project: ProjectArg,
17
+ namespace: Annotated[str | None, Argument(help="List only namespaces under this namespace.")] = None,
18
+ ):
19
+ """List Iceberg namespaces."""
20
+ import pyiceberg.exceptions
21
+
22
+ catalog = state.spiral.iceberg.catalog()
23
+
24
+ if namespace is None:
25
+ try:
26
+ namespaces = catalog.list_namespaces(project)
27
+ except pyiceberg.exceptions.ForbiddenError:
28
+ ERR_CONSOLE.print(
29
+ f"The project, {repr(project)}, does not exist or you lack the "
30
+ f"`iceberg:view` permission to list namespaces in it.",
31
+ )
32
+ raise typer.Exit(code=1)
33
+ else:
34
+ try:
35
+ namespaces = catalog.list_namespaces((project, namespace))
36
+ except pyiceberg.exceptions.ForbiddenError:
37
+ ERR_CONSOLE.print(
38
+ f"The namespace, {repr(project)}.{repr(namespace)}, does not exist or you lack the "
39
+ f"`iceberg:view` permission to list namespaces in it.",
40
+ )
41
+ raise typer.Exit(code=1)
42
+
43
+ table = CONSOLE.table.Table("Namespace ID", title="Iceberg namespaces")
44
+ for ns in namespaces:
45
+ table.add_row(".".join(ns))
46
+ CONSOLE.print(table)
47
+
48
+
49
+ @app.command(help="List tables.")
50
+ def tables(
51
+ project: ProjectArg,
52
+ namespace: Annotated[str | None, Argument(help="Show only tables in the given namespace.")] = None,
53
+ ):
54
+ import pyiceberg.exceptions
55
+
56
+ catalog = state.spiral.iceberg.catalog()
57
+
58
+ try:
59
+ if namespace is None:
60
+ tables = catalog.list_tables(project)
61
+ else:
62
+ tables = catalog.list_tables((project, namespace))
63
+ except pyiceberg.exceptions.ForbiddenError:
64
+ ERR_CONSOLE.print(
65
+ f"The namespace, {repr(project)}.{repr(namespace)}, does not exist or you lack the "
66
+ f"`iceberg:view` permission to list tables in it.",
67
+ )
68
+ raise typer.Exit(code=1)
69
+
70
+ rich_table = rich.table.Table("table id", title="Iceberg tables")
71
+ for table in tables:
72
+ rich_table.add_row(".".join(table))
73
+ CONSOLE.print(rich_table)
74
+
75
+
76
+ @app.command(help="Show the table schema.")
77
+ def schema(
78
+ project: ProjectArg,
79
+ namespace: Annotated[str, Argument(help="Table namespace.")],
80
+ table: Annotated[str, Argument(help="Table name.")],
81
+ ):
82
+ import pyiceberg.exceptions
83
+
84
+ catalog = state.spiral.iceberg.catalog()
85
+
86
+ try:
87
+ tbl = catalog.load_table((project, namespace, table))
88
+ except pyiceberg.exceptions.NoSuchTableError:
89
+ ERR_CONSOLE.print(f"No table {repr(table)} found in {repr(project)}.{repr(namespace)}", file=sys.stderr)
90
+ raise typer.Exit(code=1)
91
+
92
+ rich_table = rich.table.Table(
93
+ "Field ID", "Field name", "Type", "Required", "Doc", title=f"{project}.{namespace}.{table}"
94
+ )
95
+ for col in tbl.schema().columns:
96
+ rich_table.add_row(str(col.field_id), col.name, str(col.field_type), str(col.required), col.doc)
97
+ CONSOLE.print(rich_table)
@@ -0,0 +1,89 @@
1
+ from typing import Annotated
2
+
3
+ import questionary
4
+ import rich
5
+ import typer
6
+ from questionary import Choice
7
+ from typer import Option
8
+
9
+ from spiral.api.key_space_indexes import SyncIndexRequest
10
+ from spiral.api.projects import KeySpaceIndexResource
11
+ from spiral.api.types import IndexId
12
+ from spiral.api.workers import ResourceClass
13
+ from spiral.cli import CONSOLE, AsyncTyper, state
14
+ from spiral.cli.types import ProjectArg
15
+
16
+ app = AsyncTyper(short_help="Key Space Indexes.")
17
+
18
+
19
+ def ask_index(project_id, title="Select an index"):
20
+ indexes: list[KeySpaceIndexResource] = list(state.spiral.project(project_id).list_key_space_indexes())
21
+
22
+ if not indexes:
23
+ CONSOLE.print("[red]No indexes found[/red]")
24
+ raise typer.Exit(1)
25
+
26
+ return questionary.select(
27
+ title,
28
+ choices=[Choice(title=index.name, value=index.id) for index in sorted(indexes, key=lambda t: (t.name, t.id))],
29
+ ).ask()
30
+
31
+
32
+ def get_index_id(
33
+ project: ProjectArg,
34
+ name: Annotated[str | None, Option(help="Index name.")] = None,
35
+ ) -> IndexId:
36
+ if name is None:
37
+ return ask_index(project)
38
+
39
+ indexes: list[KeySpaceIndexResource] = list(state.spiral.project(project).list_key_space_indexes())
40
+ for index in indexes:
41
+ if index.name == name:
42
+ return index.id
43
+ raise ValueError(f"Index not found: {name}")
44
+
45
+
46
+ @app.command(help="List indexes.")
47
+ def ls(
48
+ project: ProjectArg,
49
+ ):
50
+ """List indexes."""
51
+ indexes = state.spiral.project(project).list_key_space_indexes()
52
+
53
+ rich_table = rich.table.Table("id", "name", title="Key Space Indexes")
54
+ for index in indexes:
55
+ rich_table.add_row(index.id, index.name)
56
+ CONSOLE.print(rich_table)
57
+
58
+
59
+ @app.command(help="Show index partitions.")
60
+ def show(
61
+ project: ProjectArg,
62
+ name: Annotated[str | None, Option(help="Index name.")] = None,
63
+ ):
64
+ """Show index partitions."""
65
+ index_id = get_index_id(project, name)
66
+ index = state.spiral.key_space_index(index_id)
67
+ shards = state.spiral._ops().compute_shards(index.core)
68
+
69
+ rich_table = rich.table.Table("Begin", "End", "Cardinality", title=f"Index {index.name} Partitions")
70
+ for partition in shards:
71
+ rich_table.add_row(
72
+ # TODO(marko): This isn't really pretty...
73
+ repr(partition.key_range.begin),
74
+ repr(partition.key_range.end),
75
+ str(partition.cardinality),
76
+ )
77
+ CONSOLE.print(rich_table)
78
+
79
+
80
+ @app.command(help="Trigger a sync job for an index.")
81
+ def sync(
82
+ project: ProjectArg,
83
+ name: Annotated[str | None, Option(help="Index name.")] = None,
84
+ resources: Annotated[ResourceClass, Option(help="Resources to use for the sync job.")] = ResourceClass.SMALL,
85
+ ):
86
+ """Trigger a sync job."""
87
+ index_id = get_index_id(project, name)
88
+ response = state.spiral.api.key_space_indexes.sync_index(index_id, SyncIndexRequest(resources=resources))
89
+ CONSOLE.print(f"Triggered sync job {response.worker_id} for index {index_id}.")