pyspiral 0.6.9__cp312-abi3-macosx_11_0_arm64.whl → 0.7.12__cp312-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 (58) hide show
  1. {pyspiral-0.6.9.dist-info → pyspiral-0.7.12.dist-info}/METADATA +9 -8
  2. {pyspiral-0.6.9.dist-info → pyspiral-0.7.12.dist-info}/RECORD +53 -45
  3. {pyspiral-0.6.9.dist-info → pyspiral-0.7.12.dist-info}/entry_points.txt +1 -0
  4. spiral/__init__.py +20 -0
  5. spiral/_lib.abi3.so +0 -0
  6. spiral/api/__init__.py +1 -1
  7. spiral/api/client.py +1 -1
  8. spiral/api/types.py +1 -0
  9. spiral/cli/admin.py +2 -2
  10. spiral/cli/app.py +8 -4
  11. spiral/cli/fs.py +4 -4
  12. spiral/cli/iceberg.py +1 -1
  13. spiral/cli/key_spaces.py +15 -1
  14. spiral/cli/login.py +4 -3
  15. spiral/cli/orgs.py +8 -7
  16. spiral/cli/projects.py +4 -4
  17. spiral/cli/state.py +5 -3
  18. spiral/cli/tables.py +59 -36
  19. spiral/cli/telemetry.py +1 -1
  20. spiral/cli/types.py +2 -2
  21. spiral/cli/workloads.py +3 -3
  22. spiral/client.py +69 -22
  23. spiral/core/client/__init__.pyi +48 -13
  24. spiral/core/config/__init__.pyi +47 -0
  25. spiral/core/expr/__init__.pyi +15 -0
  26. spiral/core/expr/images/__init__.pyi +3 -0
  27. spiral/core/expr/list_/__init__.pyi +4 -0
  28. spiral/core/expr/refs/__init__.pyi +4 -0
  29. spiral/core/expr/str_/__init__.pyi +3 -0
  30. spiral/core/expr/struct_/__init__.pyi +6 -0
  31. spiral/core/expr/text/__init__.pyi +5 -0
  32. spiral/core/expr/udf/__init__.pyi +14 -0
  33. spiral/core/expr/video/__init__.pyi +3 -0
  34. spiral/core/table/__init__.pyi +37 -2
  35. spiral/core/table/spec/__init__.pyi +6 -4
  36. spiral/dataloader.py +52 -38
  37. spiral/dataset.py +10 -1
  38. spiral/enrichment.py +304 -0
  39. spiral/expressions/__init__.py +21 -23
  40. spiral/expressions/base.py +9 -4
  41. spiral/expressions/file.py +17 -0
  42. spiral/expressions/http.py +11 -80
  43. spiral/expressions/s3.py +16 -0
  44. spiral/expressions/tiff.py +2 -3
  45. spiral/expressions/udf.py +38 -24
  46. spiral/iceberg.py +3 -3
  47. spiral/project.py +34 -6
  48. spiral/scan.py +80 -33
  49. spiral/settings.py +19 -97
  50. spiral/streaming_/stream.py +1 -1
  51. spiral/table.py +40 -10
  52. spiral/transaction.py +99 -2
  53. spiral/expressions/io.py +0 -100
  54. spiral/expressions/mp4.py +0 -62
  55. spiral/expressions/png.py +0 -18
  56. spiral/expressions/qoi.py +0 -18
  57. spiral/expressions/refs.py +0 -58
  58. {pyspiral-0.6.9.dist-info → pyspiral-0.7.12.dist-info}/WHEEL +0 -0
@@ -1,13 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyspiral
3
- Version: 0.6.9
3
+ Version: 0.7.12
4
4
  Classifier: Intended Audience :: Science/Research
5
5
  Classifier: Operating System :: OS Independent
6
6
  Classifier: Programming Language :: Python
7
7
  Classifier: Programming Language :: Python :: 3
8
8
  Classifier: Programming Language :: Python :: 3 :: Only
9
- Classifier: Programming Language :: Python :: 3.10
10
- Classifier: Programming Language :: Python :: 3.11
11
9
  Classifier: Programming Language :: Python :: 3.12
12
10
  Classifier: Programming Language :: Python :: 3.13
13
11
  Classifier: Programming Language :: Rust
@@ -31,20 +29,23 @@ Requires-Dist: typer>=0.16
31
29
  Requires-Dist: xxhash>=3.4.1
32
30
  Requires-Dist: polars>=1.31.0 ; extra == 'polars'
33
31
  Requires-Dist: duckdb>=1.3.2 ; extra == 'duckdb'
34
- Requires-Dist: datasets>=4.0.0 ; extra == 'datasets'
35
- Requires-Dist: pyiceberg>=0.9.1 ; extra == 'pyiceberg'
32
+ Requires-Dist: pyiceberg[s3fs]>=0.9.1 ; extra == 'iceberg'
33
+ Requires-Dist: datasets>=4.0.0 ; extra == 'huggingface'
36
34
  Requires-Dist: mosaicml-streaming>=0.13.0 ; extra == 'streaming'
37
35
  Requires-Dist: vortex-data>=0.52.1 ; extra == 'streaming'
36
+ Requires-Dist: dask>=2025.10.0 ; extra == 'dask'
37
+ Requires-Dist: distributed>=2025.10.0 ; extra == 'dask'
38
38
  Provides-Extra: polars
39
39
  Provides-Extra: duckdb
40
- Provides-Extra: datasets
41
- Provides-Extra: pyiceberg
40
+ Provides-Extra: iceberg
41
+ Provides-Extra: huggingface
42
42
  Provides-Extra: streaming
43
+ Provides-Extra: dask
43
44
  Summary: Python client for Spiral.
44
45
  Home-Page: https://spiraldb.com
45
46
  Author-email: SpiralDB <hello@spiraldb.com>
46
47
  License: Proprietary License
47
- Requires-Python: >=3.10
48
+ Requires-Python: >=3.12
48
49
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
49
50
 
50
51
  # PySpiral
@@ -1,75 +1,83 @@
1
- pyspiral-0.6.9.dist-info/METADATA,sha256=Xf1daTaPGa2O29S7SX8_P3wtFr4u26UWzTs90KHuJt4,1842
2
- pyspiral-0.6.9.dist-info/WHEEL,sha256=KQvxBiy7GLcML6Ad3w_ZPrgSvER1uXd7aYb6wy6b44Y,103
3
- pyspiral-0.6.9.dist-info/entry_points.txt,sha256=uft7u-a6g40NLt4Q6BleWbK4NY0M8nZuYPpP8DV0EOk,45
4
- spiral/__init__.py,sha256=n4JNLrO3wyw_k_U_JKyNiGON0wEpfvqxDhDdB2P6dhM,1007
5
- spiral/_lib.abi3.so,sha256=fg-GX28Vp7wNvaqJc2EBVm3_k9PQIDQ09Mqrqvd0FlM,66368032
1
+ pyspiral-0.7.12.dist-info/METADATA,sha256=pD6hAkv2h3odJlPZosBZSt8IqlCtuusYcsoOwo9v5R4,1875
2
+ pyspiral-0.7.12.dist-info/WHEEL,sha256=KQvxBiy7GLcML6Ad3w_ZPrgSvER1uXd7aYb6wy6b44Y,103
3
+ pyspiral-0.7.12.dist-info/entry_points.txt,sha256=R96Y3FpYX6XbQu9qMPfUTgiCcf4qM9OBQQZTDdBkZwA,74
4
+ spiral/__init__.py,sha256=PwaYBWFBtB7cYi7peMmhk_Lm5XzjRoLwOtLbUhc1ZDo,1449
5
+ spiral/_lib.abi3.so,sha256=WH2ysMTljTLXOUg8hBIkhm-Pj2Z30m1Scd6zBmreGUg,72162400
6
6
  spiral/adbc.py,sha256=7IxfWIeQN-fh0W5OdN_PP2x3pzQYg6ZUOLsHg3jktqw,14842
7
- spiral/api/__init__.py,sha256=ULBlVq3PnfNOO6T5naE_ULmmii-83--qTuN2PpAUQN0,2241
7
+ spiral/api/__init__.py,sha256=fguWdWeMnxWALRpqfKaUY5LmdQPsxTeyVIwIxElcRws,2240
8
8
  spiral/api/admin.py,sha256=A1iVR1XYJSObZivPAD5UzmPuMgupXc9kaHNYYa_kwfs,585
9
- spiral/api/client.py,sha256=wjxhjM2Li3UQZiWkihRKh5HHbWN4uURXXYaX_S1Bkew,4641
9
+ spiral/api/client.py,sha256=8mT1N6bn6w6E9LGV-JhjfKUPhpVskwY-5SMKV5I6olo,4641
10
10
  spiral/api/filesystems.py,sha256=yEHgHfo7t1_becm0UFedc3nd49_G77hHjYwtYQ6P9XU,4240
11
11
  spiral/api/key_space_indexes.py,sha256=-38rZXTdkL4mLhp9h3CtqyIyutzzq88tV6bhK05MqYE,640
12
12
  spiral/api/organizations.py,sha256=B-8zZ7lFJANGK7dUNbo_aU-cgI959JBP9VcWb6wdgi0,1895
13
13
  spiral/api/projects.py,sha256=1JC7VjqZJfwR6zfhBZr3OCwaf6zb-MXMOBTE_NztmcE,6356
14
14
  spiral/api/telemetry.py,sha256=tfdA3E_EWJwFVxkQfkm8tiYGRubnx2LuE5nbfsk1oG4,474
15
15
  spiral/api/text_indexes.py,sha256=_zVlGBytl-9-Unbu2POfZgLh40H1YRcagFtplgIG428,1828
16
- spiral/api/types.py,sha256=lGdiKViRgIEJXD2ubwnyEIEwHkfRumlZjVEaHMV3Tm8,682
16
+ spiral/api/types.py,sha256=HpHsoBuf7IdlXb7Dw-BkBkEvxBVIhkI8JviqhuoP9pY,696
17
17
  spiral/api/workers.py,sha256=0wZNUHMioDT53P1OBJfpjyDfIodHwwT6858z2IlRIM4,636
18
18
  spiral/api/workloads.py,sha256=XAyXV7vgZcoyyoPoGvOT4jTpyFKFMvrrAfhL6d1h1kE,1748
19
19
  spiral/arrow_.py,sha256=fdSIfIs7UjDxXZlppvOW0zz86W_70Pa5pagJilH2kOE,7583
20
20
  spiral/cli/__init__.py,sha256=LutjpWZu5Rvmba8C8bPa5vOCv74JuAoE1kvz0nd48dE,2476
21
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=smzGj5a2RwhM9RQChmlEeKZLN4Fk60-bP7Lm5_Is1Rw,2760
22
+ spiral/cli/admin.py,sha256=G_yCj8YKUiLuIZml3rqZLKS_2hBU0r4ILDdO_ZvRnCo,315
23
+ spiral/cli/app.py,sha256=eaY1SRf2wI1_T_cwrGUbldsU1QqfGrpAmN98gH1lJOE,2842
24
24
  spiral/cli/console.py,sha256=6JHbAQV6MFWz3P-VzqPOjhHpkIQagsCdzTMvmuDKMkU,2580
25
- spiral/cli/fs.py,sha256=vaPcSc2YghhHeipxNitIdsHaBhFwlwkvPFqYsFSN9P0,2927
26
- spiral/cli/iceberg.py,sha256=Q14tcGcn1LixbFCYP0GhfYwFFXTmmi8tqBPYwalJEyE,3248
27
- spiral/cli/key_spaces.py,sha256=x3IFRP5d47pKiAHeWExYMOBaT2TwxbWjVM01SUqKrwI,2943
28
- spiral/cli/login.py,sha256=2tw6uN5rEpiMMAmjQSB3-JUPf3C0Wc1eTGCDxhYtJps,731
29
- spiral/cli/orgs.py,sha256=fmOuLxpeIFfKqePRi292Gv9k-EF5pPn_tbKd2BLl2Ig,2869
25
+ spiral/cli/fs.py,sha256=chg4W6j7rPYxUiTUSVCkHoiF3vG5lcpqUKDYwp5VYtY,2919
26
+ spiral/cli/iceberg.py,sha256=wdMyl0j821MLnXNZ6Kwm65ogh98C-pjMJm3Y6YqlnTI,3249
27
+ spiral/cli/key_spaces.py,sha256=Xaw7WH-Qw_j6AxisdIoKfjAgVRXLM9qBFzuCTjPAFLI,3516
28
+ spiral/cli/login.py,sha256=2l2i38XNHGKtV4DP6PZPN4LHxceCn3AdHDE5nM2iK5M,760
29
+ spiral/cli/orgs.py,sha256=Y3hBPqBchPS72uJNn_OVx1fBIc2lO2EiKJsfRHAX1yE,2890
30
30
  spiral/cli/printer.py,sha256=aosc763hDFgoXJGkiANmNyO3kAsecAS1JWgjEhn8GCM,1784
31
- spiral/cli/projects.py,sha256=1M1nGrBT-t0aY9RV5Cnmzy7YrhIvmHwdkpa3y9j8rG8,5756
32
- spiral/cli/state.py,sha256=10wTIVQ0SJkY67Z6-KQ1LFlt3aVIPmZhoHFdTwp4kNA,130
33
- spiral/cli/tables.py,sha256=fFte_wMNcB0V-fmfSXfSbtV4UlAi-Xw5nYDJ0b62CGk,6360
34
- spiral/cli/telemetry.py,sha256=Uxo1Q1FkKJ6n6QNGOUmL3j_pRRWRx0qWIhoP-U9BuR0,589
31
+ spiral/cli/projects.py,sha256=awB2Q8gM1MtZOltWZ9ZPCbdUr-4WpsGHMR1rY12kv-s,5748
32
+ spiral/cli/state.py,sha256=3sKQuFtV2vCn3E1Dv7Sw9-IK5jiXCVBEQ9Ze17NZXDs,129
33
+ spiral/cli/tables.py,sha256=6vt6EBGt7I9b0kAQ6sQORbmWiKbRdH4ubQYjjuNBXEg,6900
34
+ spiral/cli/telemetry.py,sha256=9kp7lmimShsGoLRUic5aOEQ4hti-pPFMBFc4cdlPDmk,587
35
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=N4sQLxtQ6GYCnj00hm4VX1vUVUqzQdHhl_KfQwp-1LQ,6345
36
+ spiral/cli/types.py,sha256=o-HJhfoyPW3JD5p55yec6QNTtVGzbG-PG3kwjvZz5Zs,1358
37
+ spiral/cli/workloads.py,sha256=HL2_CkmlCm_nzTLjFogAW57Tev7RR73Qan_SZjsAoWk,2009
38
+ spiral/client.py,sha256=l5yej_59qME5RNYMXkS_eWL1XZTZ6FhgFXG5OBiGRlg,7828
39
39
  spiral/core/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  spiral/core/_tools/__init__.pyi,sha256=b2KLfTOQ67pjfbYt07o0IGiTu5o2bZw69lllV8v0Dps,143
41
41
  spiral/core/authn/__init__.pyi,sha256=z_GWyIS62fuiYQrYO8hzw4W8oGaiciqS1u5qtAt54VY,769
42
- spiral/core/client/__init__.pyi,sha256=1HK3SOMT1QKmD5Hai58ZFjiEZK0QzyYtP84hse8SBEI,6666
43
- spiral/core/table/__init__.pyi,sha256=QqG_pMlPhMtXG-56dXyQjOWVKMugPP0nnYnvYaY0Q10,3288
42
+ spiral/core/client/__init__.pyi,sha256=TzmtVmfhc0uc9A8lGvUN9VE0qb8eVOyAmNpFUL8NHs0,7529
43
+ spiral/core/config/__init__.pyi,sha256=eOzEPCKICGNNZDivC-9NJLeOVrHicIu-IVa4v4jKLxg,1242
44
+ spiral/core/expr/__init__.pyi,sha256=3HSKjkotiEkxBvGBALXEBIie0JiyI9bCpehwA3nMQkU,571
45
+ spiral/core/expr/images/__init__.pyi,sha256=wnE_wZXq7a4iqTg3SVm-ssxGw1WQZyk5dGOPaP4Btko,73
46
+ spiral/core/expr/list_/__init__.pyi,sha256=Q_9c87eIQfZbqlaw_rq3fvs93YEsW7K5VYk6VZ4g6mU,126
47
+ spiral/core/expr/refs/__init__.pyi,sha256=nZZP3l_Z6bLx6V8lTcH3Jgo--xwfADOU2XdTAvM5IMk,127
48
+ spiral/core/expr/str_/__init__.pyi,sha256=Bm6fZK-d4fNbJuuBhVoWMACXUbQQ2SjlhgrOpdOHIPM,86
49
+ spiral/core/expr/struct_/__init__.pyi,sha256=MXckd98eV_x3X0RhEWvlkA3DcDXRtLs5pNnTQkc09nE,296
50
+ spiral/core/expr/text/__init__.pyi,sha256=ed83n1xcsGY7_QDhMmJGnSQ20UrJFXcdv1AveSEcS1c,175
51
+ spiral/core/expr/udf/__init__.pyi,sha256=zsZs081KVhY3-1JidqTkWMW81Qd_ScoTGZvasIhIK-4,358
52
+ spiral/core/expr/video/__init__.pyi,sha256=nQJEcSsigZuRpMjkI_O4EEtMK_n2zRvorcL_KEeD5vU,95
53
+ spiral/core/table/__init__.pyi,sha256=h84QDg6hLuPcmRpavx5zOZM77ZCi2-YwIlrrUZJp1sE,4374
44
54
  spiral/core/table/manifests/__init__.pyi,sha256=eVfDpmhYSjafIvvALqAkZe5baN3Y1HpKpxYEbjwd4gQ,1043
45
55
  spiral/core/table/metastore/__init__.pyi,sha256=rc3u9MwEKRvL2kxOc8lBorddFRnM8o_o1frqtae86a4,1697
46
- spiral/core/table/spec/__init__.pyi,sha256=OFYJXPXix7gskYJIMog7IniZslEPJ0xvL-sUSFDPbXs,5643
47
- spiral/dataloader.py,sha256=FFZhIflQPEygXe-xBLifQnnxANi4CFooaHRm4i-EGHo,10335
48
- spiral/dataset.py,sha256=PMLoXnXuEUciP6-NXqTmQLXu0UIH7OcC4-iZtY_iuO8,7973
56
+ spiral/core/table/spec/__init__.pyi,sha256=fVuc2j3uoTdWfYNm720OfUIgrLYw9fRwj44maI5bgdY,5709
57
+ spiral/dataloader.py,sha256=W9siY4BF4p_rwTTSS4KgsaQsPLxxza6XmQhrdBzzMJ8,10592
58
+ spiral/dataset.py,sha256=S8pdiBXIhwMxQiJYgF7UI_8HkN7pZO798UzlO1LNXy4,8409
49
59
  spiral/datetime_.py,sha256=elXaUWtZuuLVcu9E0aXnvYRPB9XWqZbLDToozQYQYjU,950
50
60
  spiral/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
61
  spiral/debug/manifests.py,sha256=7f1O3ba9mrA5nXpOF9cEIQuUAteP5wiBkFy_diQJ7No,3216
52
62
  spiral/debug/metrics.py,sha256=XdRDcjggtsLNGCAjam6IxG9072pz_d2C8iLApNRFUtk,2044
53
63
  spiral/debug/scan.py,sha256=UEm_aRnql5pwDPTpZgakMLNjlzkKL4RurBFFqH_BLAQ,9526
54
- spiral/expressions/__init__.py,sha256=KhwFjVKoFgx1S6hkVcE8aZjoHY_1N-BgQ2rGEZfPQvM,7957
55
- spiral/expressions/base.py,sha256=915gpvZZCTRCO5q93pwwmhf-R6C23LQsyDt4Q2dHk9s,5290
56
- spiral/expressions/http.py,sha256=begUydWoFHEqjeLkATvI_v66Ez6_rR-OQBWO5cHbb9c,2742
57
- spiral/expressions/io.py,sha256=gJ2a0FKMmdxarWKENulPRwH7KDvSJTIh_OUxX306xAM,3045
64
+ spiral/enrichment.py,sha256=Fy8wqSIK8ZDkf7yKoNdUJpkVqSJQT1ofnfuwCZjwe58,11499
65
+ spiral/expressions/__init__.py,sha256=ZsD8g7vB0G7xy19GUiH4m79kw7KEkTQRwJl5Gn1cgtw,8049
66
+ spiral/expressions/base.py,sha256=PvhJkcUSsPSIaxirHVzM9zlqyBXiaiia1HXohXdOmL4,5377
67
+ spiral/expressions/file.py,sha256=7D9jIENJcoT0KFharBLkzK9dZgO4DYn5K_KCt0twefg,518
68
+ spiral/expressions/http.py,sha256=OOHh0WBxg3vwza_m74-rkoQWSclRMI60aPAbQ6yKZi0,486
58
69
  spiral/expressions/list_.py,sha256=MMt5lf5H1M3O-x6N_PvqOLGq9NOk6Ukv0fPWwPC_uy4,1809
59
- spiral/expressions/mp4.py,sha256=_xGVnkygddzxP9a8OACJ8_KXnejuVbYCVKBCXBQ798Y,2151
60
- spiral/expressions/png.py,sha256=KO8X0OmMzUFwpg2I_j0JTyldPzVXDWIMzjWMWDV9vIY,506
61
- spiral/expressions/qoi.py,sha256=gvIbb6fXb_Bb080sn9wkpbGGrPs2UEcTXCfuv4-kcYQ,506
62
- spiral/expressions/refs.py,sha256=omeHBQ5o6N4xgZ3x5Xz7IRrWwYBBtQY8DYK0NNAxeGo,2109
70
+ spiral/expressions/s3.py,sha256=PhQhMP-d8PLsSRtGCZbytnm7lI9VbDAbuSs2LBM4G7Q,505
63
71
  spiral/expressions/str_.py,sha256=tY8RXW3JWvr1-bEfCZtk5FAf11wKJnXPuA9EoeJ9tA4,1265
64
72
  spiral/expressions/struct.py,sha256=pGAnCDh6AK0BK1XfZ1qG4ce4ranIQEE1HQsgmzBcfwQ,2038
65
73
  spiral/expressions/text.py,sha256=-02gBWYoyNQ3qQ1--9HTa8IryUDojYQVIp8C7rgnOWQ,1893
66
- spiral/expressions/tiff.py,sha256=fQwIn0kLFBM2Y3YYIHmTgb_EIRHKT2fNc77nioDQQw4,8044
67
- spiral/expressions/udf.py,sha256=yb9MIcrFftpNDxgBF228cvdv6TY-hEFikYz2fq_nzWo,1353
74
+ spiral/expressions/tiff.py,sha256=4dngO97bT1QY0By7-PxOQVmSwQC3PQAiixVhLJ-4HMQ,7986
75
+ spiral/expressions/udf.py,sha256=XOxa7Kocb4Cg4q_qFvRT6hVnVzi22CQenqrvS-TL-VY,1936
68
76
  spiral/grpc_.py,sha256=f3czdP1Mxme42Y5--a5ogYq1TTiWn-J_MlGjwJ2mWwM,1015
69
- spiral/iceberg.py,sha256=JGq62Qnf296r9_hRAoH85GQq45-uSBjwXWw_CvPi6G4,930
77
+ spiral/iceberg.py,sha256=02OkA348eFxkEbgreeTuVlzavVvZmM4hldrZI76PZ9I,914
70
78
  spiral/iterable_dataset.py,sha256=Eekg9ad8tcwXcloHWReBbvCSr5ZappRHn2ldKTvwqS0,4622
71
79
  spiral/key_space_index.py,sha256=NAB_nONEjpMYbse8suz42w7Qb5OPHuKN9h9CT2NJe08,1460
72
- spiral/project.py,sha256=CO_Pn6vPqaonNvRdCNRFcBWr4TqO2AsAUTH5xawIeCE,7283
80
+ spiral/project.py,sha256=dkYc5iWZzz_HMKcu1EXUNNsI7hnEyGy8VrnKdVmKjjE,8199
73
81
  spiral/protogen/_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
82
  spiral/protogen/_/arrow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
83
  spiral/protogen/_/arrow/flight/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -87,16 +95,16 @@ spiral/protogen/_/substrait/extensions/__init__.py,sha256=nhnEnho70GAT8WPj2xtwJU
87
95
  spiral/protogen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
96
  spiral/protogen/util.py,sha256=smnvVo6nYH3FfDm9jqhNLaXz4bbTBaQezHQDCTvZyiQ,1486
89
97
  spiral/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- spiral/scan.py,sha256=4PUlI_DHbO1WTttLia6DinhGtOWsCiqek4ZljoEiRZc,10523
98
+ spiral/scan.py,sha256=X8CrUxCFWYzK4aL3VDM95Q7pZ5TNnYyYQYJunvARazw,12578
91
99
  spiral/server.py,sha256=ztBmB5lBnUz-smQxR_tC8AI5SOhz17wH0MI3GuzDUdM,600
92
- spiral/settings.py,sha256=JRQSwjJyNaCqQdQLxiqB_O_LZRQXMLyshJBrI2LZHwM,3113
100
+ spiral/settings.py,sha256=Ae17TH_1kKiyDo6ePjPMeLfbG2pydKSQPRuJbVkro6E,938
93
101
  spiral/snapshot.py,sha256=cTobi5jtiANxalGA-isokQHblNmXGtuUvgUGGNVybsI,1555
94
102
  spiral/streaming_/__init__.py,sha256=s7MlW2ERsuZmZGExLFL6RcZon2e0tNBocBg5ANgki7k,61
95
103
  spiral/streaming_/reader.py,sha256=tl_lC9xgh1-QFhsZn4xQT7It3PVTzHCEUT2BG2dWBRQ,4166
96
- spiral/streaming_/stream.py,sha256=nXnygiuCxi1D3PhaxV8Ujif4J9ly_OczA7CZ3W4WN2w,5913
104
+ spiral/streaming_/stream.py,sha256=DM1hBDHnWm1ZFKZ-hZ4zxeSXITcUI6kWzwdJZvywI8o,5915
97
105
  spiral/substrait_.py,sha256=AKeOD4KIXvz2J4TYxnIneOiHddtBIyOhuNxVO_uH0eg,12592
98
- spiral/table.py,sha256=G05b6M0uVmT5ew5GxuzsVB4rQzg25W3zGMTftL07pJU,11026
106
+ spiral/table.py,sha256=g6y1iV2Esk6kYhsIJfW7Wje0EGs7jgA0bVuZaSzNX1A,12068
99
107
  spiral/text_index.py,sha256=FQ9rgIEGLSJryS9lFdMhKtPFey18BXoWbPXyvZPJJ04,442
100
- spiral/transaction.py,sha256=h6YdAwOYX6qq-tXYV4i9yhy1Nq1tIfRphY_fk7Q_yLQ,1854
108
+ spiral/transaction.py,sha256=0g3xMBK2KNZVm7x6sEjeKb9U2KmlCe_-cvTwvin68Sg,5478
101
109
  spiral/types_.py,sha256=W_jyO7F6rpPiH69jhgSgV7OxQZbOlb1Ho3InpKUP6Eo,155
102
- pyspiral-0.6.9.dist-info/RECORD,,
110
+ pyspiral-0.7.12.dist-info/RECORD,,
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  spiral=spiral.cli.app:main
3
+ pyspiral=spiral.cli.app:main
spiral/__init__.py CHANGED
@@ -1,14 +1,20 @@
1
1
  """Python client for Spiral"""
2
2
 
3
+ import importlib
4
+ import os
5
+ import warnings
6
+
3
7
  # This is here to make sure we load the native extension first
4
8
  from spiral import _lib
5
9
 
6
10
  # Eagerly import the Spiral library
7
11
  assert _lib, "Spiral library"
8
12
 
13
+
9
14
  from spiral.client import Spiral # noqa: E402
10
15
  from spiral.core.client import Shard, ShuffleConfig # noqa: E402
11
16
  from spiral.dataloader import SpiralDataLoader, World # noqa: E402
17
+ from spiral.enrichment import Enrichment # noqa: E402
12
18
  from spiral.iceberg import Iceberg # noqa: E402
13
19
  from spiral.key_space_index import KeySpaceIndex # noqa: E402
14
20
  from spiral.project import Project # noqa: E402
@@ -24,6 +30,7 @@ __all__ = [
24
30
  "Table",
25
31
  "Snapshot",
26
32
  "Transaction",
33
+ "Enrichment",
27
34
  "Scan",
28
35
  "Shard",
29
36
  "ShuffleConfig",
@@ -33,3 +40,16 @@ __all__ = [
33
40
  "World",
34
41
  "Iceberg",
35
42
  ]
43
+
44
+ __version__ = importlib.metadata.version("pyspiral")
45
+
46
+
47
+ def _warn_msg():
48
+ warnings.warn(
49
+ "Spiral does not support forking, and it may cause undefined behavior. \
50
+ Please use `spawn` or `forkserver` multiprocessing."
51
+ )
52
+
53
+
54
+ if hasattr(os, "register_at_fork"):
55
+ os.register_at_fork(before=_warn_msg)
spiral/_lib.abi3.so CHANGED
Binary file
spiral/api/__init__.py CHANGED
@@ -6,7 +6,7 @@ import httpx
6
6
  from .client import _Client
7
7
 
8
8
  if TYPE_CHECKING:
9
- from spiral.core.client import Authn
9
+ from spiral.core.authn import Authn
10
10
 
11
11
  from .admin import AdminService
12
12
  from .filesystems import FileSystemService
spiral/api/client.py CHANGED
@@ -136,7 +136,7 @@ class _Client:
136
136
  method,
137
137
  path,
138
138
  params=params or {},
139
- headers={"authorization": f"Bearer {token.expose_secret()}"} if token else None,
139
+ headers={"Authorization": f"Bearer {token.expose_secret()}"} if token else None,
140
140
  **req_data,
141
141
  )
142
142
 
spiral/api/types.py CHANGED
@@ -12,6 +12,7 @@ def _validate_root_uri(uri: str) -> str:
12
12
  UserId = str
13
13
  OrgId = str
14
14
  ProjectId = str
15
+ TableId = str
15
16
  RoleId = str
16
17
  IndexId = str
17
18
  WorkerId = str
spiral/cli/admin.py CHANGED
@@ -8,7 +8,7 @@ app = AsyncTyper()
8
8
  def sync(
9
9
  org_id: OrgId | None = None,
10
10
  ):
11
- state.settings.api._admin.sync_orgs()
11
+ state.spiral.api._admin.sync_orgs()
12
12
 
13
- for membership in state.settings.api._admin.sync_memberships(org_id):
13
+ for membership in state.spiral.api._admin.sync_memberships(org_id):
14
14
  CONSOLE.print(membership)
spiral/cli/app.py CHANGED
@@ -22,7 +22,7 @@ from spiral.cli import (
22
22
  text,
23
23
  workloads,
24
24
  )
25
- from spiral.settings import LOG_DIR, PACKAGE_NAME, Settings
25
+ from spiral.settings import LOG_DIR, PACKAGE_NAME
26
26
 
27
27
  app = AsyncTyper(name="spiral")
28
28
 
@@ -63,9 +63,13 @@ def _callback(
63
63
  verbose: Annotated[
64
64
  bool | None, typer.Option("--verbose", callback=verbose_callback, help=verbose_callback.__doc__)
65
65
  ] = None,
66
- ):
67
- # Load the settings (we reload in the callback to support testing under different env vars)
68
- state.settings = Settings()
66
+ ) -> None:
67
+ # Reload the spiral client to support testing under different env vars
68
+ from spiral import Spiral
69
+ from spiral.settings import settings
70
+
71
+ config = settings()
72
+ state.spiral = Spiral(config=config)
69
73
 
70
74
 
71
75
  app.add_typer(orgs.app, name="orgs")
spiral/cli/fs.py CHANGED
@@ -17,12 +17,12 @@ app = AsyncTyper(short_help="File Systems.")
17
17
 
18
18
  @app.command(help="Show the file system configured for project.")
19
19
  def show(project: ProjectArg):
20
- file_system = state.settings.api.file_system.get_file_system(project)
20
+ file_system = state.spiral.api.file_system.get_file_system(project)
21
21
  CONSOLE.print(file_system)
22
22
 
23
23
 
24
24
  def ask_provider():
25
- res = state.settings.api.file_system.list_providers()
25
+ res = state.spiral.api.file_system.list_providers()
26
26
  return questionary.select("Select a file system provider", choices=res).ask()
27
27
 
28
28
 
@@ -66,11 +66,11 @@ def update(
66
66
  else:
67
67
  raise ValueError(f"Unknown file system type: {type_}")
68
68
 
69
- fs = state.settings.api.file_system.update_file_system(project, file_system)
69
+ fs = state.spiral.api.file_system.update_file_system(project, file_system)
70
70
  CONSOLE.print(fs)
71
71
 
72
72
 
73
73
  @app.command(help="Lists the available built-in file system providers.")
74
74
  def list_providers():
75
- for provider in state.settings.api.file_system.list_providers():
75
+ for provider in state.spiral.api.file_system.list_providers():
76
76
  CONSOLE.print(provider)
spiral/cli/iceberg.py CHANGED
@@ -8,7 +8,7 @@ from typer import Argument
8
8
  from spiral.cli import CONSOLE, ERR_CONSOLE, AsyncTyper, state
9
9
  from spiral.cli.types import ProjectArg
10
10
 
11
- app = AsyncTyper(short_help="Apache Iceberg Catalog")
11
+ app = AsyncTyper(short_help="Apache Iceberg Catalog.")
12
12
 
13
13
 
14
14
  @app.command(help="List namespaces.")
spiral/cli/key_spaces.py CHANGED
@@ -64,7 +64,7 @@ def show(
64
64
  """Show index partitions."""
65
65
  index_id = get_index_id(project, name)
66
66
  index = state.spiral.key_space_index(index_id)
67
- shards = state.spiral._ops().compute_shards(index.core)
67
+ shards = state.spiral.internal.compute_shards(index.core)
68
68
 
69
69
  rich_table = rich.table.Table("Begin", "End", "Cardinality", title=f"Index {index.name} Partitions")
70
70
  for partition in shards:
@@ -87,3 +87,17 @@ def sync(
87
87
  index_id = get_index_id(project, name)
88
88
  response = state.spiral.api.key_space_indexes.sync_index(index_id, SyncIndexRequest(resources=resources))
89
89
  CONSOLE.print(f"Triggered sync job {response.worker_id} for index {index_id}.")
90
+
91
+
92
+ # TODO(marko): This will be removed.
93
+ @app.command(help="Run a sync and wait for it to complete.")
94
+ def sync_local(
95
+ project: ProjectArg,
96
+ name: Annotated[str | None, Option(help="Index name.")] = None,
97
+ ):
98
+ """Run a sync and wait for it to complete."""
99
+ index_id = get_index_id(project, name)
100
+ index = state.spiral.key_space_index(index_id)
101
+ snapshot = state.spiral.table(index.table_id).snapshot()
102
+ state.spiral.internal.update_key_space_index(index.core, snapshot.core)
103
+ CONSOLE.print(f"Index {index.name} is up to date as-of {snapshot.asof}.")
spiral/cli/login.py CHANGED
@@ -1,10 +1,11 @@
1
1
  import jwt
2
2
 
3
3
  from spiral.cli import CONSOLE, state
4
+ from spiral.core.authn import DeviceCodeAuth
4
5
 
5
6
 
6
7
  def command(org_id: str | None = None, force: bool = False, show_token: bool = False):
7
- token = state.settings.device_code_auth.authenticate(force=force, org_id=org_id)
8
+ token = DeviceCodeAuth.default().authenticate(force=force, org_id=org_id)
8
9
  CONSOLE.print("Successfully logged in.")
9
10
  if show_token:
10
11
  CONSOLE.print(token.expose_secret(), soft_wrap=True)
@@ -12,7 +13,7 @@ def command(org_id: str | None = None, force: bool = False, show_token: bool = F
12
13
 
13
14
  def whoami():
14
15
  """Display the current user's information."""
15
- payload = jwt.decode(state.settings.authn.token().expose_secret(), options={"verify_signature": False})
16
+ payload = jwt.decode(state.spiral.authn.token().expose_secret(), options={"verify_signature": False})
16
17
 
17
18
  if "org_id" in payload:
18
19
  CONSOLE.print(f"{payload['org_id']}")
@@ -20,5 +21,5 @@ def whoami():
20
21
 
21
22
 
22
23
  def logout():
23
- state.settings.device_code_auth.logout()
24
+ DeviceCodeAuth.default().logout()
24
25
  CONSOLE.print("Logged out.")
spiral/cli/orgs.py CHANGED
@@ -9,13 +9,14 @@ from typer import Option
9
9
  from spiral.api.organizations import CreateOrgRequest, InviteUserRequest, OrgRole, PortalLinkIntent, PortalLinkRequest
10
10
  from spiral.cli import CONSOLE, ERR_CONSOLE, AsyncTyper, state
11
11
  from spiral.cli.types import OrganizationArg
12
+ from spiral.core.authn import DeviceCodeAuth
12
13
 
13
14
  app = AsyncTyper(short_help="Org admin.")
14
15
 
15
16
 
16
17
  @app.command(help="Switch the active organization.")
17
18
  def switch(org_id: OrganizationArg):
18
- state.settings.device_code_auth.authenticate(org_id=org_id)
19
+ DeviceCodeAuth.default().authenticate(org_id=org_id)
19
20
  CONSOLE.print(f"Switched to organization: {org_id}")
20
21
 
21
22
 
@@ -23,10 +24,10 @@ def switch(org_id: OrganizationArg):
23
24
  def create(
24
25
  name: Annotated[str | None, Option(help="The human-readable name of the organization.")] = None,
25
26
  ):
26
- res = state.settings.api.organization.create(CreateOrgRequest(name=name))
27
+ res = state.spiral.api.organization.create(CreateOrgRequest(name=name))
27
28
 
28
29
  # Authenticate to the new organization
29
- state.settings.device_code_auth.authenticate(org_id=res.org.id)
30
+ DeviceCodeAuth.default().authenticate(org_id=res.org.id)
30
31
 
31
32
  CONSOLE.print(f"{res.org.name} [dim]{res.org.id}[/dim]")
32
33
 
@@ -36,7 +37,7 @@ def ls():
36
37
  org_id = current_org_id()
37
38
 
38
39
  table = Table("", "id", "name", "role", title="Organizations")
39
- for m in state.settings.api.organization.list_memberships():
40
+ for m in state.spiral.api.organization.list_memberships():
40
41
  table.add_row("👉" if m.org.id == org_id else "", m.org.id, m.org.name, m.role)
41
42
 
42
43
  CONSOLE.print(table)
@@ -44,7 +45,7 @@ def ls():
44
45
 
45
46
  @app.command(help="Invite a user to the organization.")
46
47
  def invite(email: str, role: OrgRole = OrgRole.MEMBER, expires_in_days: int = 7):
47
- state.settings.api.organization.invite_user(
48
+ state.spiral.api.organization.invite_user(
48
49
  InviteUserRequest(email=email, role=role, expires_in_days=expires_in_days)
49
50
  )
50
51
  CONSOLE.print(f"Invited {email} as a {role.value}.")
@@ -76,13 +77,13 @@ def domains():
76
77
 
77
78
 
78
79
  def _do_action(intent: PortalLinkIntent):
79
- res = state.settings.api.organization.portal_link(PortalLinkRequest(intent=intent))
80
+ res = state.spiral.api.organization.portal_link(PortalLinkRequest(intent=intent))
80
81
  CONSOLE.print(f"Opening the configuration portal:\n{res.url}")
81
82
  webbrowser.open(res.url)
82
83
 
83
84
 
84
85
  def current_org_id():
85
- if token := state.settings.authn.token():
86
+ if token := state.spiral.authn.token():
86
87
  if org_id := jwt.decode(token.expose_secret(), options={"verify_signature": False}).get("org_id"):
87
88
  return org_id
88
89
  ERR_CONSOLE.print("You are not logged in to an organization.")
spiral/cli/projects.py CHANGED
@@ -27,7 +27,7 @@ app = AsyncTyper(short_help="Projects and grants.")
27
27
 
28
28
  @app.command(help="List projects.")
29
29
  def ls():
30
- projects = list(state.settings.api.project.list())
30
+ projects = list(state.spiral.api.project.list())
31
31
  CONSOLE.print(printer.table_of_models(Project, projects))
32
32
 
33
33
 
@@ -38,7 +38,7 @@ def create(
38
38
  ] = None,
39
39
  name: Annotated[str | None, Option(help="Friendly name for the project.")] = None,
40
40
  ):
41
- res = state.settings.api.project.create(CreateProjectRequest(id_prefix=id_prefix, name=name))
41
+ res = state.spiral.api.project.create(CreateProjectRequest(id_prefix=id_prefix, name=name))
42
42
  CONSOLE.print(f"Created project {res.project.id}")
43
43
 
44
44
 
@@ -130,7 +130,7 @@ def grant(
130
130
  else:
131
131
  raise ValueError("Invalid grant principal")
132
132
 
133
- state.settings.api.project.grant_role(
133
+ state.spiral.api.project.grant_role(
134
134
  project,
135
135
  GrantRoleRequest(
136
136
  role_id=role,
@@ -143,5 +143,5 @@ def grant(
143
143
 
144
144
  @app.command(help="List project grants.")
145
145
  def grants(project: ProjectArg):
146
- project_grants = list(state.settings.api.project.list_grants(project))
146
+ project_grants = list(state.spiral.api.project.list_grants(project))
147
147
  CONSOLE.print(printer.table_of_models(Grant, project_grants, title="Project Grants"))
spiral/cli/state.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from spiral import Spiral
2
- from spiral.settings import Settings
2
+ from spiral.settings import settings
3
3
 
4
- settings: Settings = Settings()
5
- spiral: Spiral = Spiral(settings)
4
+
5
+ @property
6
+ def spiral() -> Spiral:
7
+ return Spiral(settings())
spiral/cli/tables.py CHANGED
@@ -1,5 +1,5 @@
1
- import datetime
2
- from typing import Annotated, Literal
1
+ from collections.abc import Callable
2
+ from typing import Annotated
3
3
 
4
4
  import questionary
5
5
  import rich
@@ -60,6 +60,60 @@ def ls(
60
60
  CONSOLE.print(rich_table)
61
61
 
62
62
 
63
+ @app.command(help="Show the leading rows of the table.")
64
+ def head(
65
+ project: ProjectArg,
66
+ table: Annotated[str | None, Option(help="Table name.")] = None,
67
+ dataset: Annotated[str | None, Option(help="Dataset name.")] = None,
68
+ n: Annotated[int, Option("-n", help="Maximum number of rows to show. Defaults to 10.")] = 10,
69
+ ):
70
+ import polars as pl
71
+
72
+ _, t = get_table(project, table, dataset)
73
+
74
+ with pl.Config(tbl_rows=-1):
75
+ CONSOLE.print(t.to_polars().limit(n).collect())
76
+
77
+
78
+ def validate_non_empty_str(text: str) -> bool | str:
79
+ if len(text) > 0:
80
+ return True
81
+
82
+ return "Must provide at least one character."
83
+
84
+
85
+ def get_string(message: str, validate: Callable[[str], bool | str] = validate_non_empty_str) -> str:
86
+ return questionary.text(message, validate=validate).ask()
87
+
88
+
89
+ @app.command(help="Move table to a different dataset.")
90
+ def move(
91
+ project: ProjectArg,
92
+ table: Annotated[str | None, Option(help="Table name.")] = None,
93
+ dataset: Annotated[str | None, Option(help="Dataset name.")] = None,
94
+ new_dataset: Annotated[str | None, Option(help="New dataset name.")] = None,
95
+ ):
96
+ identifier, _ = get_table(project, table, dataset)
97
+ new_dataset = get_string("Provide a new dataset name")
98
+
99
+ state.spiral.project(project).move_table(identifier, new_dataset)
100
+ CONSOLE.print("Success.")
101
+
102
+
103
+ @app.command(help="Rename table.")
104
+ def rename(
105
+ project: ProjectArg,
106
+ table: Annotated[str | None, Option(help="Table name.")] = None,
107
+ dataset: Annotated[str | None, Option(help="Dataset name.")] = None,
108
+ new_table: Annotated[str | None, Option(help="New table name.")] = None,
109
+ ):
110
+ identifier, _ = get_table(project, table, dataset)
111
+ new_table = get_string("Provide a new table name")
112
+
113
+ state.spiral.project(project).rename_table(identifier, new_table)
114
+ CONSOLE.print("Success.")
115
+
116
+
63
117
  @app.command(help="Show the table key schema.")
64
118
  def key_schema(
65
119
  project: ProjectArg,
@@ -97,40 +151,9 @@ def flush(
97
151
  project: ProjectArg,
98
152
  table: Annotated[str | None, Option(help="Table name.")] = None,
99
153
  dataset: Annotated[str | None, Option(help="Dataset name.")] = None,
100
- keep: Annotated[
101
- Literal["1h", "2h", "4h"] | None,
102
- Option(help="Duration string that indicates how much WAL to keep. Defaults to 24h."),
103
- ] = None,
104
- full: Annotated[bool, Option(help="Flush full Write-Ahead-Log.")] = False,
105
154
  ):
106
- # TODO(marko): Use some human-readable duration parsing library.
107
- duration = None
108
- if keep is not None:
109
- if full:
110
- raise ValueError("Cannot specify both --keep and --full")
111
- match keep:
112
- case "1h":
113
- duration = datetime.timedelta(hours=1)
114
- case "2h":
115
- duration = datetime.timedelta(hours=2)
116
- case "4h":
117
- duration = datetime.timedelta(hours=4)
118
- case _:
119
- raise ValueError(f"Invalid duration string: {keep}")
120
-
121
- if full:
122
- # Warn and wait for confirmation.
123
- ERR_CONSOLE.print("[bold yellow]Warning: All currently open transaction will fail to commit.[/bold yellow]")
124
- if not questionary.confirm("Are you sure you want to continue?", default=False).ask(): # pyright: ignore[reportAny]
125
- ERR_CONSOLE.print("Aborting.")
126
- raise typer.Exit(1)
127
-
128
- duration = datetime.timedelta(hours=0)
129
-
130
- keep_latest_s = int(duration.total_seconds()) if duration is not None else None
131
-
132
155
  identifier, t = get_table(project, table, dataset)
133
- state.spiral._ops().flush_wal(t.core, keep_latest_s=keep_latest_s) # pyright: ignore[reportPrivateUsage]
156
+ state.spiral.internal.flush_wal(t.core) # pyright: ignore[reportPrivateUsage]
134
157
  CONSOLE.print(f"Flushed WAL for table {identifier} in project {project}.")
135
158
 
136
159
 
@@ -143,10 +166,10 @@ def manifests(
143
166
  _, t = get_table(project, table, dataset)
144
167
  s = t.snapshot()
145
168
 
146
- key_space_state = state.spiral._ops().key_space_state(s.core) # pyright: ignore[reportPrivateUsage]
169
+ key_space_state = state.spiral.internal.key_space_state(s.core) # pyright: ignore[reportPrivateUsage]
147
170
  key_space_manifest = key_space_state.manifest
148
171
 
149
- column_groups_states = state.spiral._ops().column_groups_states(s.core, key_space_state) # pyright: ignore[reportPrivateUsage]
172
+ column_groups_states = state.spiral.internal.column_groups_states(s.core, key_space_state) # pyright: ignore[reportPrivateUsage]
150
173
  display_manifests(key_space_manifest, [(x.column_group, x.manifest) for x in column_groups_states])
151
174
 
152
175