pyspiral 0.7.18__cp312-abi3-manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. pyspiral-0.7.18.dist-info/METADATA +52 -0
  2. pyspiral-0.7.18.dist-info/RECORD +110 -0
  3. pyspiral-0.7.18.dist-info/WHEEL +4 -0
  4. pyspiral-0.7.18.dist-info/entry_points.txt +3 -0
  5. spiral/__init__.py +55 -0
  6. spiral/_lib.abi3.so +0 -0
  7. spiral/adbc.py +411 -0
  8. spiral/api/__init__.py +78 -0
  9. spiral/api/admin.py +15 -0
  10. spiral/api/client.py +164 -0
  11. spiral/api/filesystems.py +134 -0
  12. spiral/api/key_space_indexes.py +23 -0
  13. spiral/api/organizations.py +77 -0
  14. spiral/api/projects.py +219 -0
  15. spiral/api/telemetry.py +19 -0
  16. spiral/api/text_indexes.py +56 -0
  17. spiral/api/types.py +23 -0
  18. spiral/api/workers.py +40 -0
  19. spiral/api/workloads.py +52 -0
  20. spiral/arrow_.py +216 -0
  21. spiral/cli/__init__.py +88 -0
  22. spiral/cli/__main__.py +4 -0
  23. spiral/cli/admin.py +14 -0
  24. spiral/cli/app.py +108 -0
  25. spiral/cli/console.py +95 -0
  26. spiral/cli/fs.py +76 -0
  27. spiral/cli/iceberg.py +97 -0
  28. spiral/cli/key_spaces.py +103 -0
  29. spiral/cli/login.py +25 -0
  30. spiral/cli/orgs.py +90 -0
  31. spiral/cli/printer.py +53 -0
  32. spiral/cli/projects.py +147 -0
  33. spiral/cli/state.py +7 -0
  34. spiral/cli/tables.py +197 -0
  35. spiral/cli/telemetry.py +17 -0
  36. spiral/cli/text.py +115 -0
  37. spiral/cli/types.py +50 -0
  38. spiral/cli/workloads.py +58 -0
  39. spiral/client.py +256 -0
  40. spiral/core/__init__.pyi +0 -0
  41. spiral/core/_tools/__init__.pyi +5 -0
  42. spiral/core/authn/__init__.pyi +21 -0
  43. spiral/core/client/__init__.pyi +285 -0
  44. spiral/core/config/__init__.pyi +35 -0
  45. spiral/core/expr/__init__.pyi +15 -0
  46. spiral/core/expr/images/__init__.pyi +3 -0
  47. spiral/core/expr/list_/__init__.pyi +4 -0
  48. spiral/core/expr/refs/__init__.pyi +4 -0
  49. spiral/core/expr/str_/__init__.pyi +3 -0
  50. spiral/core/expr/struct_/__init__.pyi +6 -0
  51. spiral/core/expr/text/__init__.pyi +5 -0
  52. spiral/core/expr/udf/__init__.pyi +14 -0
  53. spiral/core/expr/video/__init__.pyi +3 -0
  54. spiral/core/table/__init__.pyi +141 -0
  55. spiral/core/table/manifests/__init__.pyi +35 -0
  56. spiral/core/table/metastore/__init__.pyi +58 -0
  57. spiral/core/table/spec/__init__.pyi +215 -0
  58. spiral/dataloader.py +299 -0
  59. spiral/dataset.py +264 -0
  60. spiral/datetime_.py +27 -0
  61. spiral/debug/__init__.py +0 -0
  62. spiral/debug/manifests.py +87 -0
  63. spiral/debug/metrics.py +56 -0
  64. spiral/debug/scan.py +266 -0
  65. spiral/enrichment.py +306 -0
  66. spiral/expressions/__init__.py +274 -0
  67. spiral/expressions/base.py +167 -0
  68. spiral/expressions/file.py +17 -0
  69. spiral/expressions/http.py +17 -0
  70. spiral/expressions/list_.py +68 -0
  71. spiral/expressions/s3.py +16 -0
  72. spiral/expressions/str_.py +39 -0
  73. spiral/expressions/struct.py +59 -0
  74. spiral/expressions/text.py +62 -0
  75. spiral/expressions/tiff.py +222 -0
  76. spiral/expressions/udf.py +60 -0
  77. spiral/grpc_.py +32 -0
  78. spiral/iceberg.py +31 -0
  79. spiral/iterable_dataset.py +106 -0
  80. spiral/key_space_index.py +44 -0
  81. spiral/project.py +227 -0
  82. spiral/protogen/_/__init__.py +0 -0
  83. spiral/protogen/_/arrow/__init__.py +0 -0
  84. spiral/protogen/_/arrow/flight/__init__.py +0 -0
  85. spiral/protogen/_/arrow/flight/protocol/__init__.py +0 -0
  86. spiral/protogen/_/arrow/flight/protocol/sql/__init__.py +2548 -0
  87. spiral/protogen/_/google/__init__.py +0 -0
  88. spiral/protogen/_/google/protobuf/__init__.py +2310 -0
  89. spiral/protogen/_/message_pool.py +3 -0
  90. spiral/protogen/_/py.typed +0 -0
  91. spiral/protogen/_/scandal/__init__.py +190 -0
  92. spiral/protogen/_/spfs/__init__.py +72 -0
  93. spiral/protogen/_/spql/__init__.py +61 -0
  94. spiral/protogen/_/substrait/__init__.py +6196 -0
  95. spiral/protogen/_/substrait/extensions/__init__.py +169 -0
  96. spiral/protogen/__init__.py +0 -0
  97. spiral/protogen/util.py +41 -0
  98. spiral/py.typed +0 -0
  99. spiral/scan.py +363 -0
  100. spiral/server.py +17 -0
  101. spiral/settings.py +36 -0
  102. spiral/snapshot.py +56 -0
  103. spiral/streaming_/__init__.py +3 -0
  104. spiral/streaming_/reader.py +133 -0
  105. spiral/streaming_/stream.py +157 -0
  106. spiral/substrait_.py +274 -0
  107. spiral/table.py +224 -0
  108. spiral/text_index.py +17 -0
  109. spiral/transaction.py +155 -0
  110. spiral/types_.py +6 -0
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyspiral
3
+ Version: 0.7.18
4
+ Classifier: Intended Audience :: Science/Research
5
+ Classifier: Operating System :: OS Independent
6
+ Classifier: Programming Language :: Python
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3 :: Only
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Rust
12
+ Classifier: License :: Other/Proprietary License
13
+ Requires-Dist: betterproto2>=0.9.0
14
+ Requires-Dist: google-re2>=1.1.20240702
15
+ Requires-Dist: grpclib>=0.4.7
16
+ Requires-Dist: hishel>=0.0.30
17
+ Requires-Dist: httpx>=0.27.0
18
+ Requires-Dist: nanoid>=2.0.0
19
+ Requires-Dist: numpy>=2
20
+ Requires-Dist: pyarrow>=21.0.0
21
+ Requires-Dist: pydantic-settings>=2.3.4
22
+ Requires-Dist: pydantic[email]>=2.5.3,<2.12
23
+ Requires-Dist: pyjwt[crypto]>=2.9.0
24
+ Requires-Dist: pyperclip>=1.9.0
25
+ Requires-Dist: questionary>=2.0.1
26
+ Requires-Dist: sqlglot[rs]>=25.25.1
27
+ Requires-Dist: tqdm>=4.66.5
28
+ Requires-Dist: typer>=0.16
29
+ Requires-Dist: xxhash>=3.4.1
30
+ Requires-Dist: polars>=1.31.0 ; extra == 'polars'
31
+ Requires-Dist: duckdb>=1.3.2 ; extra == 'duckdb'
32
+ Requires-Dist: pyiceberg[s3fs]>=0.9.1 ; extra == 'iceberg'
33
+ Requires-Dist: datasets>=4.0.0 ; extra == 'huggingface'
34
+ Requires-Dist: mosaicml-streaming>=0.13.0 ; extra == 'streaming'
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
+ Provides-Extra: polars
39
+ Provides-Extra: duckdb
40
+ Provides-Extra: iceberg
41
+ Provides-Extra: huggingface
42
+ Provides-Extra: streaming
43
+ Provides-Extra: dask
44
+ Summary: Python client for Spiral.
45
+ Home-Page: https://spiraldb.com
46
+ Author-email: SpiralDB <hello@spiraldb.com>
47
+ License: Proprietary License
48
+ Requires-Python: >=3.12
49
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
50
+
51
+ # PySpiral
52
+
@@ -0,0 +1,110 @@
1
+ pyspiral-0.7.18.dist-info/METADATA,sha256=H8JfgI2_gAyURsln1Hpb0K30ai_Re6o5IrRlDaBENyI,1875
2
+ pyspiral-0.7.18.dist-info/WHEEL,sha256=ydlpo1_yEJ2g1Axq3LoOd_OfioJa2swc2j5IDCa4uho,107
3
+ pyspiral-0.7.18.dist-info/entry_points.txt,sha256=R96Y3FpYX6XbQu9qMPfUTgiCcf4qM9OBQQZTDdBkZwA,74
4
+ spiral/__init__.py,sha256=PwaYBWFBtB7cYi7peMmhk_Lm5XzjRoLwOtLbUhc1ZDo,1449
5
+ spiral/_lib.abi3.so,sha256=ve7Wb1brnhkNdV6tk6FfVsdzmmyTSYHPMNB7Rr0WwSw,69709704
6
+ spiral/adbc.py,sha256=7IxfWIeQN-fh0W5OdN_PP2x3pzQYg6ZUOLsHg3jktqw,14842
7
+ spiral/api/__init__.py,sha256=fguWdWeMnxWALRpqfKaUY5LmdQPsxTeyVIwIxElcRws,2240
8
+ spiral/api/admin.py,sha256=A1iVR1XYJSObZivPAD5UzmPuMgupXc9kaHNYYa_kwfs,585
9
+ spiral/api/client.py,sha256=8mT1N6bn6w6E9LGV-JhjfKUPhpVskwY-5SMKV5I6olo,4641
10
+ spiral/api/filesystems.py,sha256=yEHgHfo7t1_becm0UFedc3nd49_G77hHjYwtYQ6P9XU,4240
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=1JC7VjqZJfwR6zfhBZr3OCwaf6zb-MXMOBTE_NztmcE,6356
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=HpHsoBuf7IdlXb7Dw-BkBkEvxBVIhkI8JviqhuoP9pY,696
17
+ spiral/api/workers.py,sha256=0wZNUHMioDT53P1OBJfpjyDfIodHwwT6858z2IlRIM4,636
18
+ spiral/api/workloads.py,sha256=XAyXV7vgZcoyyoPoGvOT4jTpyFKFMvrrAfhL6d1h1kE,1748
19
+ spiral/arrow_.py,sha256=fdSIfIs7UjDxXZlppvOW0zz86W_70Pa5pagJilH2kOE,7583
20
+ spiral/cli/__init__.py,sha256=LutjpWZu5Rvmba8C8bPa5vOCv74JuAoE1kvz0nd48dE,2476
21
+ spiral/cli/__main__.py,sha256=kNaKM2xgJo7GRogf83nYldLM-RGUR6vymdGwZxywQu0,71
22
+ spiral/cli/admin.py,sha256=G_yCj8YKUiLuIZml3rqZLKS_2hBU0r4ILDdO_ZvRnCo,315
23
+ spiral/cli/app.py,sha256=eaY1SRf2wI1_T_cwrGUbldsU1QqfGrpAmN98gH1lJOE,2842
24
+ spiral/cli/console.py,sha256=6JHbAQV6MFWz3P-VzqPOjhHpkIQagsCdzTMvmuDKMkU,2580
25
+ spiral/cli/fs.py,sha256=chg4W6j7rPYxUiTUSVCkHoiF3vG5lcpqUKDYwp5VYtY,2919
26
+ spiral/cli/iceberg.py,sha256=wdMyl0j821MLnXNZ6Kwm65ogh98C-pjMJm3Y6YqlnTI,3249
27
+ spiral/cli/key_spaces.py,sha256=84MibTdjI5bFK7lhL0w1WOlw-uBZtFnPPlTQuI2PPGw,3524
28
+ spiral/cli/login.py,sha256=2l2i38XNHGKtV4DP6PZPN4LHxceCn3AdHDE5nM2iK5M,760
29
+ spiral/cli/orgs.py,sha256=Y3hBPqBchPS72uJNn_OVx1fBIc2lO2EiKJsfRHAX1yE,2890
30
+ spiral/cli/printer.py,sha256=aosc763hDFgoXJGkiANmNyO3kAsecAS1JWgjEhn8GCM,1784
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
+ spiral/cli/text.py,sha256=DlWGe4JrkdERAiqyITNpk91Wqb63Re99rNYlIFsIamc,4031
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=Z3SNrX5UnU2UshFuN6jsb6N1RvC8Llb8Uvn4O3Nuvg4,8940
39
+ spiral/core/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ spiral/core/_tools/__init__.pyi,sha256=b2KLfTOQ67pjfbYt07o0IGiTu5o2bZw69lllV8v0Dps,143
41
+ spiral/core/authn/__init__.pyi,sha256=deZvPlCyiPC6PpXxpEZVglxL5mUJ1Qqg20ieEQgU6ik,582
42
+ spiral/core/client/__init__.pyi,sha256=pyTcqvaPSV8g0HsjEJfkKkemCDtNZO-EI5t31AQhJZM,7851
43
+ spiral/core/config/__init__.pyi,sha256=1BaB7fTGly_fW-qTSQtxbGrYErzkqxuJokDFPupP7d0,955
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=BVQwMdvppPBIWhzN1JNbzAwxGarybpXFlR1AvllGXTM,4584
54
+ spiral/core/table/manifests/__init__.pyi,sha256=eVfDpmhYSjafIvvALqAkZe5baN3Y1HpKpxYEbjwd4gQ,1043
55
+ spiral/core/table/metastore/__init__.pyi,sha256=rc3u9MwEKRvL2kxOc8lBorddFRnM8o_o1frqtae86a4,1697
56
+ spiral/core/table/spec/__init__.pyi,sha256=fVuc2j3uoTdWfYNm720OfUIgrLYw9fRwj44maI5bgdY,5709
57
+ spiral/dataloader.py,sha256=W9siY4BF4p_rwTTSS4KgsaQsPLxxza6XmQhrdBzzMJ8,10592
58
+ spiral/dataset.py,sha256=xse0evrNDKPXNrqaS5ZklyvPsrTPaFov5A2uwwMd9sU,8429
59
+ spiral/datetime_.py,sha256=elXaUWtZuuLVcu9E0aXnvYRPB9XWqZbLDToozQYQYjU,950
60
+ spiral/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ spiral/debug/manifests.py,sha256=7f1O3ba9mrA5nXpOF9cEIQuUAteP5wiBkFy_diQJ7No,3216
62
+ spiral/debug/metrics.py,sha256=XdRDcjggtsLNGCAjam6IxG9072pz_d2C8iLApNRFUtk,2044
63
+ spiral/debug/scan.py,sha256=UEm_aRnql5pwDPTpZgakMLNjlzkKL4RurBFFqH_BLAQ,9526
64
+ spiral/enrichment.py,sha256=vA0NPOHvYq6QB7UnQoStNvxCMYgPkoA-C1OgSicLwlU,11718
65
+ spiral/expressions/__init__.py,sha256=ZsD8g7vB0G7xy19GUiH4m79kw7KEkTQRwJl5Gn1cgtw,8049
66
+ spiral/expressions/base.py,sha256=Kidz5VmtW1sow_sRlnzuoZpmE-N3RZvcc9CuYp0Odhg,5518
67
+ spiral/expressions/file.py,sha256=7D9jIENJcoT0KFharBLkzK9dZgO4DYn5K_KCt0twefg,518
68
+ spiral/expressions/http.py,sha256=OOHh0WBxg3vwza_m74-rkoQWSclRMI60aPAbQ6yKZi0,486
69
+ spiral/expressions/list_.py,sha256=MMt5lf5H1M3O-x6N_PvqOLGq9NOk6Ukv0fPWwPC_uy4,1809
70
+ spiral/expressions/s3.py,sha256=PhQhMP-d8PLsSRtGCZbytnm7lI9VbDAbuSs2LBM4G7Q,505
71
+ spiral/expressions/str_.py,sha256=tY8RXW3JWvr1-bEfCZtk5FAf11wKJnXPuA9EoeJ9tA4,1265
72
+ spiral/expressions/struct.py,sha256=pGAnCDh6AK0BK1XfZ1qG4ce4ranIQEE1HQsgmzBcfwQ,2038
73
+ spiral/expressions/text.py,sha256=-02gBWYoyNQ3qQ1--9HTa8IryUDojYQVIp8C7rgnOWQ,1893
74
+ spiral/expressions/tiff.py,sha256=4dngO97bT1QY0By7-PxOQVmSwQC3PQAiixVhLJ-4HMQ,7986
75
+ spiral/expressions/udf.py,sha256=5mu8azZ7pLI3D9YaXFH33ukIY37BdohC1eFfyLBTNx0,1925
76
+ spiral/grpc_.py,sha256=f3czdP1Mxme42Y5--a5ogYq1TTiWn-J_MlGjwJ2mWwM,1015
77
+ spiral/iceberg.py,sha256=02OkA348eFxkEbgreeTuVlzavVvZmM4hldrZI76PZ9I,914
78
+ spiral/iterable_dataset.py,sha256=Eekg9ad8tcwXcloHWReBbvCSr5ZappRHn2ldKTvwqS0,4622
79
+ spiral/key_space_index.py,sha256=NAB_nONEjpMYbse8suz42w7Qb5OPHuKN9h9CT2NJe08,1460
80
+ spiral/project.py,sha256=dkYc5iWZzz_HMKcu1EXUNNsI7hnEyGy8VrnKdVmKjjE,8199
81
+ spiral/protogen/_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
+ spiral/protogen/_/arrow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
+ spiral/protogen/_/arrow/flight/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
+ spiral/protogen/_/arrow/flight/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ spiral/protogen/_/arrow/flight/protocol/sql/__init__.py,sha256=ooZZsDCRFpktUCH11OdxMRa_GLQYnY9w-1fBr5a7vBk,90023
86
+ spiral/protogen/_/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
+ spiral/protogen/_/google/protobuf/__init__.py,sha256=H0FVEXusqww2j5dl7Ee05tR6qMG_hQioUp1qFfDgnco,80036
88
+ spiral/protogen/_/message_pool.py,sha256=4-cRhhiM6bmfpUJZ8qxc8LEyqHBHpLCcotjbyZxl7JM,71
89
+ spiral/protogen/_/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
+ spiral/protogen/_/scandal/__init__.py,sha256=liUQAICLd2sPccCmqo0_c1duSbNj_m8p_IgmdnHsB3E,4965
91
+ spiral/protogen/_/spfs/__init__.py,sha256=zMMEDIfPXQNBkisLI-iMWbJABye-vK42Gf2BUQQYR_c,2028
92
+ spiral/protogen/_/spql/__init__.py,sha256=PEC4bI-PHdJ4Zd8Jb1k6Xk2iFYoYqIUbTGlL2JVGnT0,1548
93
+ spiral/protogen/_/substrait/__init__.py,sha256=-ngqHcYfio6s1B4M1_e1VsDymUcFK9qdM17ECA31qLw,209837
94
+ spiral/protogen/_/substrait/extensions/__init__.py,sha256=nhnEnho70GAT8WPj2xtwJUzk5GJ6X2e-HTvyk7emGsk,5326
95
+ spiral/protogen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
+ spiral/protogen/util.py,sha256=smnvVo6nYH3FfDm9jqhNLaXz4bbTBaQezHQDCTvZyiQ,1486
97
+ spiral/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
+ spiral/scan.py,sha256=eOM0Ksn2p7McHzyeXDOfmIAl73Nx4kHDJYnYjv-noD4,14070
99
+ spiral/server.py,sha256=ztBmB5lBnUz-smQxR_tC8AI5SOhz17wH0MI3GuzDUdM,600
100
+ spiral/settings.py,sha256=Ae17TH_1kKiyDo6ePjPMeLfbG2pydKSQPRuJbVkro6E,938
101
+ spiral/snapshot.py,sha256=cTobi5jtiANxalGA-isokQHblNmXGtuUvgUGGNVybsI,1555
102
+ spiral/streaming_/__init__.py,sha256=s7MlW2ERsuZmZGExLFL6RcZon2e0tNBocBg5ANgki7k,61
103
+ spiral/streaming_/reader.py,sha256=tl_lC9xgh1-QFhsZn4xQT7It3PVTzHCEUT2BG2dWBRQ,4166
104
+ spiral/streaming_/stream.py,sha256=DM1hBDHnWm1ZFKZ-hZ4zxeSXITcUI6kWzwdJZvywI8o,5915
105
+ spiral/substrait_.py,sha256=AKeOD4KIXvz2J4TYxnIneOiHddtBIyOhuNxVO_uH0eg,12592
106
+ spiral/table.py,sha256=teT_Ib4kNQT2D4jWRq_Jurni0sAqfv1QZjaBiqeLVr4,8060
107
+ spiral/text_index.py,sha256=FQ9rgIEGLSJryS9lFdMhKtPFey18BXoWbPXyvZPJJ04,442
108
+ spiral/transaction.py,sha256=o8aaN_TcvS9hT34ms8wA90Js4SCRV8h9BM-CL-rAZsg,5529
109
+ spiral/types_.py,sha256=W_jyO7F6rpPiH69jhgSgV7OxQZbOlb1Ho3InpKUP6Eo,155
110
+ pyspiral-0.7.18.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.9.6)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-abi3-manylinux_2_28_x86_64
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ spiral=spiral.cli.app:main
3
+ pyspiral=spiral.cli.app:main
spiral/__init__.py ADDED
@@ -0,0 +1,55 @@
1
+ """Python client for Spiral"""
2
+
3
+ import importlib
4
+ import os
5
+ import warnings
6
+
7
+ # This is here to make sure we load the native extension first
8
+ from spiral import _lib
9
+
10
+ # Eagerly import the Spiral library
11
+ assert _lib, "Spiral library"
12
+
13
+
14
+ from spiral.client import Spiral # noqa: E402
15
+ from spiral.core.client import Shard, ShuffleConfig # noqa: E402
16
+ from spiral.dataloader import SpiralDataLoader, World # noqa: E402
17
+ from spiral.enrichment import Enrichment # noqa: E402
18
+ from spiral.iceberg import Iceberg # noqa: E402
19
+ from spiral.key_space_index import KeySpaceIndex # noqa: E402
20
+ from spiral.project import Project # noqa: E402
21
+ from spiral.scan import Scan # noqa: E402
22
+ from spiral.snapshot import Snapshot # noqa: E402
23
+ from spiral.table import Table # noqa: E402
24
+ from spiral.text_index import TextIndex # noqa: E402
25
+ from spiral.transaction import Transaction # noqa: E402
26
+
27
+ __all__ = [
28
+ "Spiral",
29
+ "Project",
30
+ "Table",
31
+ "Snapshot",
32
+ "Transaction",
33
+ "Enrichment",
34
+ "Scan",
35
+ "Shard",
36
+ "ShuffleConfig",
37
+ "TextIndex",
38
+ "KeySpaceIndex",
39
+ "SpiralDataLoader",
40
+ "World",
41
+ "Iceberg",
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 ADDED
Binary file
spiral/adbc.py ADDED
@@ -0,0 +1,411 @@
1
+ import abc
2
+ import functools
3
+ import logging
4
+ from concurrent.futures import ThreadPoolExecutor
5
+ from urllib.parse import urlparse
6
+
7
+ import pyarrow as pa
8
+ import pyarrow.compute as pc
9
+ import sqlglot
10
+ import sqlglot.expressions as exp
11
+ from pyarrow.flight import (
12
+ Action,
13
+ FlightDescriptor,
14
+ FlightEndpoint,
15
+ FlightError,
16
+ FlightInfo,
17
+ FlightMetadataWriter,
18
+ FlightServerBase,
19
+ MetadataRecordBatchReader,
20
+ RecordBatchStream,
21
+ ServerCallContext,
22
+ Ticket,
23
+ )
24
+
25
+ from spiral import Spiral
26
+ from spiral.api.projects import TableResource
27
+ from spiral.protogen._.arrow.flight.protocol import sql as rpc
28
+ from spiral.protogen._.arrow.flight.protocol.sql import (
29
+ CommandGetCatalogs,
30
+ CommandGetDbSchemas,
31
+ CommandGetSqlInfo,
32
+ CommandGetTables,
33
+ CommandStatementQuery,
34
+ SqlInfo,
35
+ SqlSupportedTransaction,
36
+ )
37
+ from spiral.protogen._.google.protobuf import Any
38
+ from spiral.snapshot import Snapshot
39
+
40
+ log = logging.getLogger(__name__)
41
+ logging.getLogger("sqlx").setLevel(logging.WARNING)
42
+
43
+
44
+ def debuggable(func):
45
+ """A decorator to enable GUI (i.e. PyCharm) debugging in the
46
+ decorated Arrow Flight RPC Server function.
47
+
48
+ See: https://github.com/apache/arrow/issues/36844
49
+ for more details...
50
+ """
51
+
52
+ @functools.wraps(func)
53
+ def wrapper_decorator(*args, **kwargs):
54
+ try:
55
+ import pydevd
56
+
57
+ pydevd.connected = True
58
+ pydevd.settrace(suspend=False)
59
+ except ImportError:
60
+ # Not running in debugger
61
+ pass
62
+ value = func(*args, **kwargs)
63
+ return value
64
+
65
+ return wrapper_decorator
66
+
67
+
68
+ class ADBCServerBase:
69
+ def get_sql_info(self, _req: CommandGetSqlInfo) -> pa.RecordBatchReader:
70
+ """Default implementation that reports no support for any complex features."""
71
+ info = {
72
+ SqlInfo.FLIGHT_SQL_SERVER_NAME: "Spiral ADBC Server",
73
+ SqlInfo.FLIGHT_SQL_SERVER_VERSION: "0.0.1",
74
+ SqlInfo.FLIGHT_SQL_SERVER_ARROW_VERSION: pa.__version__,
75
+ SqlInfo.FLIGHT_SQL_SERVER_READ_ONLY: True,
76
+ SqlInfo.FLIGHT_SQL_SERVER_TRANSACTION: SqlSupportedTransaction.NONE.value,
77
+ }
78
+
79
+ # See https://github.com/apache/arrow-adbc/blob/38c21c2311a59803559cb0091b3f34180c28b25f/rust/core/src/schemas.rs#L35
80
+ union_fields = [
81
+ pa.field("string_value", pa.string()),
82
+ pa.field("bool_value", pa.bool_()),
83
+ pa.field("int64_value", pa.int64()),
84
+ pa.field("int32_bitmask", pa.int32()),
85
+ pa.field("string_list", pa.list_(pa.string())),
86
+ pa.field(
87
+ "int32_to_int32_list_map",
88
+ pa.map_(pa.int32(), pa.list_(pa.int32()), keys_sorted=False),
89
+ ),
90
+ ]
91
+ schema = pa.schema(
92
+ [
93
+ pa.field("info_name", pa.uint32(), nullable=False),
94
+ pa.field("info_value", pa.dense_union(union_fields), nullable=False),
95
+ ]
96
+ )
97
+
98
+ # PyArrow doesn't support creating a dense union for us :(
99
+ types = []
100
+ offsets = []
101
+ ints = []
102
+ bools = []
103
+ strs = []
104
+ for value in info.values():
105
+ if isinstance(value, str):
106
+ types.append(0)
107
+ offsets.append(len(strs))
108
+ strs.append(value)
109
+ elif isinstance(value, bool):
110
+ types.append(1)
111
+ offsets.append(len(bools))
112
+ bools.append(value)
113
+ else:
114
+ types.append(1)
115
+ offsets.append(len(ints))
116
+ ints.append(value)
117
+
118
+ values = pa.UnionArray.from_dense(
119
+ pa.array(types, type=pa.int8()),
120
+ pa.array(offsets, type=pa.int32()),
121
+ [pa.array(data, type=f.type) for data, f in zip([strs, bools, ints, [], [], []], union_fields)],
122
+ [f.name for f in union_fields],
123
+ )
124
+
125
+ return pa.table(data=[pa.array(list(info.keys()), type=pa.uint32()), values], schema=schema).to_reader()
126
+
127
+ @abc.abstractmethod
128
+ def get_catalogs(self, req: CommandGetCatalogs) -> pa.RecordBatchReader: ...
129
+
130
+ @abc.abstractmethod
131
+ def get_db_schemas(self, req: CommandGetDbSchemas) -> pa.RecordBatchReader: ...
132
+
133
+ @abc.abstractmethod
134
+ def get_tables(self, req: CommandGetTables) -> pa.RecordBatchReader: ...
135
+
136
+ @abc.abstractmethod
137
+ def statement_query(self, req: CommandStatementQuery, limit: int | None = None) -> pa.RecordBatchReader: ...
138
+
139
+
140
+ class SpiralADBCServer(ADBCServerBase):
141
+ def __init__(self, spiral: Spiral):
142
+ self.sp = spiral
143
+
144
+ self.pool = ThreadPoolExecutor()
145
+
146
+ def open_snapshot(self, tbl) -> Snapshot:
147
+ """Open a table in the Spiral project and return it as a PyArrow Dataset."""
148
+ if tbl.catalog is None or tbl.catalog == "":
149
+ raise FlightError("Project (Data Catalog) must be specified to open a table.")
150
+
151
+ project = tbl.catalog
152
+ dataset = tbl.db or "default"
153
+ table = tbl.name
154
+
155
+ return self.sp.project(project).table(f"{dataset}.{table}").snapshot()
156
+
157
+ def get_catalogs(self, req: CommandGetCatalogs) -> pa.RecordBatchReader:
158
+ schema = pa.schema([pa.field("catalog_name", pa.string(), nullable=False)])
159
+
160
+ @debuggable
161
+ def batches():
162
+ yield pa.RecordBatch.from_arrays(
163
+ [[p.id for p in self.sp.list_projects()]],
164
+ schema=schema,
165
+ )
166
+
167
+ return pa.RecordBatchReader.from_batches(schema, batches())
168
+
169
+ def get_db_schemas(self, req: CommandGetDbSchemas) -> pa.RecordBatchReader:
170
+ """Get the schemas from the database."""
171
+
172
+ schema = pa.schema(
173
+ [
174
+ pa.field("catalog_name", pa.string()),
175
+ pa.field("db_schema_name", pa.string(), nullable=False),
176
+ ]
177
+ )
178
+
179
+ @debuggable
180
+ def batches():
181
+ if req.catalog == "":
182
+ # Empty string means databases _without_ a catalog, which we don't support
183
+ return
184
+ catalog = req.catalog
185
+
186
+ # Otherwise, catalog is either the project ID, or None.
187
+ if catalog is None:
188
+ projects = self.sp.list_projects()
189
+ else:
190
+ projects = [self.sp.project(req.catalog)]
191
+
192
+ for project in projects:
193
+ datasets = {tbl.dataset for tbl in project.list_tables()}
194
+
195
+ batch = pa.RecordBatch.from_arrays(
196
+ [
197
+ [project.id] * len(datasets),
198
+ list(datasets),
199
+ ],
200
+ schema=schema,
201
+ )
202
+
203
+ if req.db_schema_filter_pattern:
204
+ mask = pc.match_like(batch["db_schema_name"], req.db_schema_filter_pattern)
205
+ batch = batch.filter(mask)
206
+
207
+ yield batch
208
+
209
+ return pa.RecordBatchReader.from_batches(schema, batches())
210
+
211
+ def get_tables(self, req: CommandGetTables) -> pa.RecordBatchReader:
212
+ schema = pa.schema(
213
+ [
214
+ pa.field("catalog_name", pa.string()),
215
+ pa.field("db_schema_name", pa.string()),
216
+ pa.field("table_name", pa.string(), nullable=False),
217
+ pa.field("table_type", pa.string(), nullable=False),
218
+ ]
219
+ + [pa.field("table_schema", pa.binary(), nullable=False)]
220
+ if req.include_schema
221
+ else []
222
+ )
223
+
224
+ @debuggable
225
+ def batches():
226
+ if req.catalog == "":
227
+ # Empty string means databases _without_ a catalog, which we don't support
228
+ return
229
+
230
+ if req.catalog is None:
231
+ projects = list(self.sp.list_projects())
232
+ else:
233
+ projects = [self.sp.project(req.catalog)]
234
+ projects = sorted(projects, key=lambda p: p.id)
235
+
236
+ def _process_project(project):
237
+ tables: list[TableResource] = project.list_tables()
238
+
239
+ rows = []
240
+ for table in tables:
241
+ row = {
242
+ "catalog_name": project.id,
243
+ "db_schema_name": table.dataset,
244
+ "table_name": table.table,
245
+ "table_type": "TABLE",
246
+ }
247
+
248
+ if req.include_schema:
249
+ open_table = project.table(f"{table.dataset}.{table.table}")
250
+ row["table_schema"] = open_table.snapshot().to_dataset().schema.serialize().to_pybytes()
251
+
252
+ rows.append(row)
253
+
254
+ return pa.RecordBatch.from_pylist(rows, schema=schema)
255
+
256
+ yield from self.pool.map(_process_project, projects)
257
+
258
+ return pa.RecordBatchReader.from_batches(schema, batches())
259
+
260
+ @debuggable
261
+ def statement_query(self, req: CommandStatementQuery, limit: int | None = None) -> pa.RecordBatchReader:
262
+ # Extract the tables from the query, and bring them into the Python locals scope.
263
+ expr = sqlglot.parse_one(req.query, dialect="duckdb")
264
+ datasets = {}
265
+ for tbl in expr.find_all(exp.Table):
266
+ # We swap the three-part identifier out for a single identifier
267
+ # This lets us register a PyArrow Dataset with DuckDB for the query.
268
+ snapshot = self.open_snapshot(tbl)
269
+ name = snapshot.table.table_id
270
+ datasets[name] = snapshot.to_dataset()
271
+ tbl.replace(exp.table_(table=name))
272
+
273
+ try:
274
+ import duckdb
275
+ except ImportError:
276
+ raise FlightError("DuckDB is required for SQL queries.")
277
+
278
+ try:
279
+ # Create a DuckDB connection and register the datasets
280
+ conn = duckdb.connect()
281
+ for name, dataset in datasets.items():
282
+ conn.register(name, dataset)
283
+ sql = conn.sql(expr.sql(dialect="duckdb"))
284
+ except Exception as e:
285
+ raise FlightError(str(e))
286
+
287
+ if limit is not None:
288
+ sql = sql.limit(limit)
289
+
290
+ return sql.fetch_arrow_reader(batch_size=1_000)
291
+
292
+
293
+ class ADBCFlightServer(FlightServerBase):
294
+ """An implementation of a FlightSQL ADBC server."""
295
+
296
+ def __init__(self, abdc: ADBCServerBase, *, location=None, **kwargs):
297
+ super().__init__(location=location, **kwargs)
298
+ self.location = location
299
+ self.adbc = abdc
300
+
301
+ self.host = "localhost"
302
+ self.tls = False
303
+ if location:
304
+ parts = urlparse(location)
305
+ self.host = parts.hostname
306
+ self.tls = parts.scheme.endswith("s")
307
+
308
+ @debuggable
309
+ def do_action(self, context: ServerCallContext, action: Action):
310
+ log.info("DoAction %s: %s", context.peer(), action)
311
+ super().do_action(context, action)
312
+
313
+ @debuggable
314
+ def do_exchange(self, context: ServerCallContext, descriptor: FlightDescriptor, reader, writer):
315
+ log.info("DoExchange %s: %s", context.peer(), descriptor)
316
+ super().do_exchange(context, descriptor, reader, writer)
317
+
318
+ @debuggable
319
+ def do_get(self, context: ServerCallContext, ticket: Ticket):
320
+ log.info("DoGet %s: %s", context.peer(), ticket)
321
+ req = self.parse_command(ticket.ticket)
322
+ match req:
323
+ case CommandGetSqlInfo():
324
+ return RecordBatchStream(self.adbc.get_sql_info(req))
325
+ case CommandGetCatalogs():
326
+ return RecordBatchStream(self.adbc.get_catalogs(req))
327
+ case CommandGetDbSchemas():
328
+ return RecordBatchStream(self.adbc.get_db_schemas(req))
329
+ case CommandGetTables():
330
+ return RecordBatchStream(self.adbc.get_tables(req))
331
+ case CommandStatementQuery():
332
+ return RecordBatchStream(self.adbc.statement_query(req))
333
+ case _:
334
+ raise NotImplementedError(f"Unsupported do_Get: {req}")
335
+
336
+ @debuggable
337
+ def do_put(
338
+ self,
339
+ context: ServerCallContext,
340
+ descriptor: FlightDescriptor,
341
+ reader: MetadataRecordBatchReader,
342
+ writer: FlightMetadataWriter,
343
+ ):
344
+ log.info("DoPut %s: %s", context.peer(), descriptor)
345
+ super().do_put(context, descriptor, reader, writer)
346
+
347
+ @debuggable
348
+ def get_flight_info(self, context: ServerCallContext, descriptor: FlightDescriptor) -> FlightInfo:
349
+ log.info("GetFlightInfo %s: %s", context.peer(), descriptor)
350
+ req = self.parse_command(descriptor.command)
351
+ match req:
352
+ case CommandGetSqlInfo():
353
+ # Each metadata type contributes to the schema.
354
+ schema = self.adbc.get_sql_info(req).schema
355
+ case CommandGetCatalogs():
356
+ schema = self.adbc.get_catalogs(req).schema
357
+ case CommandGetDbSchemas():
358
+ schema = self.adbc.get_db_schemas(req).schema
359
+ case CommandGetTables():
360
+ schema = self.adbc.get_tables(req).schema
361
+ case CommandStatementQuery():
362
+ schema = self.adbc.statement_query(req, limit=0).schema
363
+ case _:
364
+ raise NotImplementedError(f"Unsupported command: {req}")
365
+
366
+ return self._make_flight_info(self.descriptor_to_key(descriptor), descriptor, schema)
367
+
368
+ @staticmethod
369
+ def parse_command(command: bytes):
370
+ command = Any().parse(command)
371
+
372
+ if not command.type_url.startswith("type.googleapis.com/arrow.flight.protocol.sql."):
373
+ raise NotImplementedError(f"Unsupported command: {command.type_url}")
374
+
375
+ proto_cls_name = command.type_url[len("type.googleapis.com/arrow.flight.protocol.sql.") :]
376
+ proto_cls = getattr(rpc, proto_cls_name)
377
+ return proto_cls().parse(command.value)
378
+
379
+ @staticmethod
380
+ def descriptor_to_key(descriptor):
381
+ return descriptor.command
382
+
383
+ @debuggable
384
+ def get_schema(self, context: ServerCallContext, descriptor: FlightDescriptor):
385
+ log.info("GetSchema %s: %s", context.peer(), descriptor)
386
+ return super().get_schema(context, descriptor)
387
+
388
+ @debuggable
389
+ def list_actions(self, context: ServerCallContext):
390
+ log.info("ListActions %s", context.peer())
391
+ super().list_actions(context)
392
+
393
+ @debuggable
394
+ def list_flights(self, context: ServerCallContext, criteria):
395
+ log.info("ListFlights %s: %s", context.peer(), criteria)
396
+ super().list_flights(context, criteria)
397
+
398
+ def _make_flight_info(self, key, descriptor, schema: pa.Schema):
399
+ # If we pass zero locations, the FlightSQL client should attempt to use the original connection.
400
+ endpoints = [FlightEndpoint(key, [])]
401
+ return FlightInfo(schema, descriptor, endpoints, -1, -1)
402
+
403
+
404
+ if __name__ == "__main__":
405
+ import logging
406
+
407
+ logging.basicConfig()
408
+ logging.getLogger("spiral").setLevel(logging.DEBUG)
409
+
410
+ server = ADBCFlightServer(SpiralADBCServer(Spiral()), location="grpc://localhost:5005")
411
+ server.serve()