pyspiral 0.3.1__cp310-abi3-macosx_11_0_arm64.whl → 0.4.1__cp310-abi3-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {pyspiral-0.3.1.dist-info → pyspiral-0.4.1.dist-info}/METADATA +9 -13
  2. pyspiral-0.4.1.dist-info/RECORD +98 -0
  3. {pyspiral-0.3.1.dist-info → pyspiral-0.4.1.dist-info}/WHEEL +1 -1
  4. spiral/__init__.py +6 -9
  5. spiral/_lib.abi3.so +0 -0
  6. spiral/adbc.py +21 -14
  7. spiral/api/__init__.py +14 -175
  8. spiral/api/admin.py +12 -26
  9. spiral/api/client.py +160 -0
  10. spiral/api/filesystems.py +100 -72
  11. spiral/api/organizations.py +45 -58
  12. spiral/api/projects.py +171 -134
  13. spiral/api/telemetry.py +19 -0
  14. spiral/api/types.py +20 -0
  15. spiral/api/workloads.py +32 -25
  16. spiral/{arrow.py → arrow_.py} +12 -0
  17. spiral/cli/__init__.py +2 -5
  18. spiral/cli/admin.py +7 -12
  19. spiral/cli/app.py +23 -6
  20. spiral/cli/console.py +1 -1
  21. spiral/cli/fs.py +82 -17
  22. spiral/cli/iceberg/__init__.py +7 -0
  23. spiral/cli/iceberg/namespaces.py +47 -0
  24. spiral/cli/iceberg/tables.py +60 -0
  25. spiral/cli/indexes/__init__.py +19 -0
  26. spiral/cli/login.py +14 -5
  27. spiral/cli/orgs.py +90 -0
  28. spiral/cli/printer.py +9 -1
  29. spiral/cli/projects.py +136 -0
  30. spiral/cli/state.py +2 -0
  31. spiral/cli/tables/__init__.py +121 -0
  32. spiral/cli/telemetry.py +18 -0
  33. spiral/cli/types.py +8 -10
  34. spiral/cli/{workload.py → workloads.py} +11 -11
  35. spiral/{catalog.py → client.py} +23 -37
  36. spiral/core/client/__init__.pyi +117 -0
  37. spiral/core/index/__init__.pyi +15 -0
  38. spiral/core/{core → table}/__init__.pyi +44 -17
  39. spiral/core/{manifests → table/manifests}/__init__.pyi +5 -23
  40. spiral/core/table/metastore/__init__.pyi +62 -0
  41. spiral/core/{spec → table/spec}/__init__.pyi +41 -66
  42. spiral/datetime_.py +27 -0
  43. spiral/expressions/__init__.py +26 -18
  44. spiral/expressions/base.py +5 -5
  45. spiral/expressions/list_.py +1 -1
  46. spiral/expressions/mp4.py +2 -9
  47. spiral/expressions/png.py +1 -1
  48. spiral/expressions/qoi.py +1 -1
  49. spiral/expressions/refs.py +3 -9
  50. spiral/expressions/struct.py +7 -5
  51. spiral/expressions/text.py +62 -0
  52. spiral/expressions/udf.py +3 -3
  53. spiral/iceberg/__init__.py +3 -0
  54. spiral/iceberg/client.py +33 -0
  55. spiral/indexes/__init__.py +5 -0
  56. spiral/indexes/client.py +137 -0
  57. spiral/indexes/index.py +34 -0
  58. spiral/indexes/scan.py +22 -0
  59. spiral/project.py +19 -110
  60. spiral/{proto → protogen}/_/scandal/__init__.py +23 -135
  61. spiral/protogen/_/spiral/table/__init__.py +22 -0
  62. spiral/protogen/substrait/__init__.py +3399 -0
  63. spiral/protogen/substrait/extensions/__init__.py +115 -0
  64. spiral/server.py +17 -0
  65. spiral/settings.py +29 -91
  66. spiral/substrait_.py +9 -5
  67. spiral/tables/__init__.py +12 -0
  68. spiral/tables/client.py +130 -0
  69. spiral/{dataset.py → tables/dataset.py} +9 -199
  70. spiral/tables/debug/manifests.py +70 -0
  71. spiral/tables/debug/metrics.py +56 -0
  72. spiral/{debug.py → tables/debug/scan.py} +6 -9
  73. spiral/{maintenance.py → tables/maintenance.py} +1 -1
  74. spiral/{scan_.py → tables/scan.py} +63 -89
  75. spiral/tables/snapshot.py +78 -0
  76. spiral/{table.py → tables/table.py} +59 -73
  77. spiral/{txn.py → tables/transaction.py} +7 -3
  78. pyspiral-0.3.1.dist-info/RECORD +0 -85
  79. spiral/api/tables.py +0 -91
  80. spiral/api/tokens.py +0 -56
  81. spiral/authn/authn.py +0 -89
  82. spiral/authn/device.py +0 -206
  83. spiral/authn/github_.py +0 -33
  84. spiral/authn/modal_.py +0 -18
  85. spiral/cli/org.py +0 -90
  86. spiral/cli/project.py +0 -109
  87. spiral/cli/table.py +0 -20
  88. spiral/cli/token.py +0 -27
  89. spiral/core/metastore/__init__.pyi +0 -91
  90. spiral/proto/_/spfs/__init__.py +0 -36
  91. spiral/proto/_/spiral/table/__init__.py +0 -276
  92. spiral/proto/_/spiraldb/metastore/__init__.py +0 -499
  93. spiral/proto/__init__.py +0 -0
  94. spiral/proto/scandal/__init__.py +0 -45
  95. spiral/proto/spiral/__init__.py +0 -0
  96. spiral/proto/spiral/table/__init__.py +0 -96
  97. {pyspiral-0.3.1.dist-info → pyspiral-0.4.1.dist-info}/entry_points.txt +0 -0
  98. /spiral/{authn/__init__.py → core/__init__.pyi} +0 -0
  99. /spiral/{core → protogen/_}/__init__.py +0 -0
  100. /spiral/{proto/_ → protogen/_/arrow}/__init__.py +0 -0
  101. /spiral/{proto/_/arrow → protogen/_/arrow/flight}/__init__.py +0 -0
  102. /spiral/{proto/_/arrow/flight → protogen/_/arrow/flight/protocol}/__init__.py +0 -0
  103. /spiral/{proto → protogen}/_/arrow/flight/protocol/sql/__init__.py +0 -0
  104. /spiral/{proto/_/arrow/flight/protocol → protogen/_/spiral}/__init__.py +0 -0
  105. /spiral/{proto → protogen/_}/substrait/__init__.py +0 -0
  106. /spiral/{proto → protogen/_}/substrait/extensions/__init__.py +0 -0
  107. /spiral/{proto/_/spiral → protogen}/__init__.py +0 -0
  108. /spiral/{proto → protogen}/util.py +0 -0
  109. /spiral/{proto/_/spiraldb → tables/debug}/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyspiral
3
- Version: 0.3.1
3
+ Version: 0.4.1
4
4
  Classifier: Intended Audience :: Science/Research
5
5
  Classifier: Operating System :: OS Independent
6
6
  Classifier: Programming Language :: Python
@@ -17,34 +17,30 @@ Requires-Dist: google-re2>=1.1.20240702
17
17
  Requires-Dist: grpclib>=0.4.7
18
18
  Requires-Dist: hishel>=0.0.30
19
19
  Requires-Dist: httpx>=0.27.0
20
- Requires-Dist: numpy>=1.26.3
21
- Requires-Dist: pyarrow>=17.0.0
20
+ Requires-Dist: numpy>=2
21
+ Requires-Dist: pyarrow>=21.0.0
22
22
  Requires-Dist: pydantic-settings>=2.3.4
23
23
  Requires-Dist: pydantic[email]>=2.5.3
24
24
  Requires-Dist: pyjwt[crypto]>=2.9.0
25
25
  Requires-Dist: questionary>=2.0.1
26
26
  Requires-Dist: tqdm>=4.66.5
27
- Requires-Dist: typer>=0.12.3
27
+ Requires-Dist: typer>=0.16
28
28
  Requires-Dist: xxhash>=3.4.1
29
29
  Requires-Dist: nanoid>=2.0.0
30
30
  Requires-Dist: sqlglot[rs]>=25.25.1
31
31
  Requires-Dist: pyperclip>=1.9.0
32
- Requires-Dist: boto3>=1.35.36
33
- Requires-Dist: polars>=1.6.0 ; extra == 'polars'
34
- Requires-Dist: duckdb>=1.1.1 ; extra == 'duckdb'
35
- Requires-Dist: pyiceberg>=0.9.0 ; extra == 'pyiceberg'
32
+ Requires-Dist: polars>=1.31.0 ; extra == 'polars'
33
+ Requires-Dist: duckdb>=1.3.2 ; extra == 'duckdb'
34
+ Requires-Dist: pyiceberg>=0.9.1 ; extra == 'pyiceberg'
36
35
  Provides-Extra: polars
37
36
  Provides-Extra: duckdb
38
37
  Provides-Extra: pyiceberg
39
- Summary: Python implementation of Spiral table format.
40
- Keywords: vortex,spiraldb
38
+ Summary: Python client for Spiral.
41
39
  Home-Page: https://spiraldb.com
42
- Author: Spiral<hello@spiraldb.com>
43
40
  Author-email: SpiralDB <hello@spiraldb.com>
44
- License: Proprietary
41
+ License: Proprietary License
45
42
  Requires-Python: >=3.10
46
43
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
47
- Project-URL: Source Code, https://github.com/spiraldb/spiraldb
48
44
 
49
45
  # PySpiral
50
46
 
@@ -0,0 +1,98 @@
1
+ pyspiral-0.4.1.dist-info/METADATA,sha256=nPb5bevTsjXvNMRCM-l_rtrImARfU0_P7DXuLo-KT1U,1610
2
+ pyspiral-0.4.1.dist-info/WHEEL,sha256=Mdosfxua6Dx1zYgObRH97e3wyiELqBbLtoRJj4RUSQE,103
3
+ pyspiral-0.4.1.dist-info/entry_points.txt,sha256=uft7u-a6g40NLt4Q6BleWbK4NY0M8nZuYPpP8DV0EOk,45
4
+ spiral/__init__.py,sha256=Jv1vbcnnmcTsBLN5mSNjnX3ae4C_mgojXDSBFaqIhN0,208
5
+ spiral/_lib.abi3.so,sha256=dSWSUTQtz-KlVIdBtYHLyblCuf_j5HxFM3Gt_7G_BTA,61525328
6
+ spiral/adbc.py,sha256=HcvR60uQeEK2oggSAK6y5VYtIrACIiCQ-85MEf18EZc,14199
7
+ spiral/api/__init__.py,sha256=_7BS1RhqEFjnt3XwFWZNCHVEQeSKpezPevAiGCsvDbE,1776
8
+ spiral/api/admin.py,sha256=A1iVR1XYJSObZivPAD5UzmPuMgupXc9kaHNYYa_kwfs,585
9
+ spiral/api/client.py,sha256=9-L6T8niQAXo90jRxllJD4hXXmcGfHj7CW9X3XTYa5Q,4551
10
+ spiral/api/filesystems.py,sha256=5Ky_otnresGj7WdsR8Xi7DDM3lkB8UES6Lru_xWAGDM,4559
11
+ spiral/api/organizations.py,sha256=B-8zZ7lFJANGK7dUNbo_aU-cgI959JBP9VcWb6wdgi0,1895
12
+ spiral/api/projects.py,sha256=JBGof9A2Ivasu2jrULMjHBwlna0M8WRrTNqU-Es4GJ8,5673
13
+ spiral/api/telemetry.py,sha256=tfdA3E_EWJwFVxkQfkm8tiYGRubnx2LuE5nbfsk1oG4,474
14
+ spiral/api/types.py,sha256=zx-BRKsi1GHg9aL9gMUaVQWYYMXJcP0A8OQUc7jSIAc,653
15
+ spiral/api/workloads.py,sha256=XAyXV7vgZcoyyoPoGvOT4jTpyFKFMvrrAfhL6d1h1kE,1748
16
+ spiral/arrow_.py,sha256=T1LZ7bh9aMDbXfpUsf0dR0E1roTQyAYSgZ2mL4s8J_4,7681
17
+ spiral/cli/__init__.py,sha256=ooAFz_iCpVCKHE0TiVElIynbP2PtTgD9cUw46Vh1lcw,2145
18
+ spiral/cli/__main__.py,sha256=kNaKM2xgJo7GRogf83nYldLM-RGUR6vymdGwZxywQu0,71
19
+ spiral/cli/admin.py,sha256=7WbU_tr05clUjmZ-RkKTlvcf1pbXIElRfHRJlCItFGk,326
20
+ spiral/cli/app.py,sha256=-k0rrLbfJRLay_2_MOCt57PLcx0VnNMCkrnKV7j7nos,1725
21
+ spiral/cli/console.py,sha256=6JHbAQV6MFWz3P-VzqPOjhHpkIQagsCdzTMvmuDKMkU,2580
22
+ spiral/cli/fs.py,sha256=dVPoAoAbuQ9yJlfI-JiFgS9VdnPmeBMygVHgehJRj34,4367
23
+ spiral/cli/iceberg/__init__.py,sha256=IQV_gwCFSj6Ubxs58VM9Pal1ymgG2bxdDgOPuk9E5bs,214
24
+ spiral/cli/iceberg/namespaces.py,sha256=x9pvHlcXtcATYYjqimHa6CtkyL3taQUJ--ni_Bfoemc,1510
25
+ spiral/cli/iceberg/tables.py,sha256=nSR4-t54otJfCmubB6vXnbOkbqPVGV0sHBlc-t9cIVg,1930
26
+ spiral/cli/indexes/__init__.py,sha256=-USfxCIdckzZKBNQ-DXqe3V5ttWVo_Fsa1Mfcx5hdIw,467
27
+ spiral/cli/login.py,sha256=InKMnpV8NATW5RPgB3ZL-DSVPzUuUByyK4Fx7pZEgfg,607
28
+ spiral/cli/orgs.py,sha256=V-4ZTT3FwFQLcs1-BenC8uCgvWOJcxkZPSdCPfsexhc,2848
29
+ spiral/cli/printer.py,sha256=W83KAE-7meoDD1yRltLQrZqrA2olGapBGy_2USWkY08,1778
30
+ spiral/cli/projects.py,sha256=TKXu_VzkIUccwXzdlg-wQMkrB-Py33g052NrbuJx-D4,5096
31
+ spiral/cli/state.py,sha256=10wTIVQ0SJkY67Z6-KQ1LFlt3aVIPmZhoHFdTwp4kNA,130
32
+ spiral/cli/tables/__init__.py,sha256=lkGLDeU28IVnuxJdlYSUh6QSB9fQ4_1MeZJL73iXcHo,3660
33
+ spiral/cli/telemetry.py,sha256=ABDCyV5QJGOIJp4AxvK0LG5xNPIysP37K5haL38T7P4,586
34
+ spiral/cli/types.py,sha256=YG1eHhRLaqlVU_18DQBuF_YMsabhMZLBY0V9CvbSxjY,1369
35
+ spiral/cli/workloads.py,sha256=SbxgwiBlX1AuqpOLV3gs7DFkH-Tbeend7qJTwq0Je84,1994
36
+ spiral/client.py,sha256=K-OuMOTgYxOA9vef5jSANjmPRBfGrzQ65fg6Fd-rHMY,2683
37
+ spiral/core/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ spiral/core/client/__init__.pyi,sha256=FFpUCPyQjH_VxldflqWmvniSugLuDiQv11vMim9HhDY,3466
39
+ spiral/core/index/__init__.pyi,sha256=NPOG1ztFO6siBGpmJU3boRzX26xfxw--2TiCydosGvo,314
40
+ spiral/core/table/__init__.pyi,sha256=dwOaxcOl6ZIlxoLjOnC3CNUgGetWfnEV1Jx06aCH8M8,3265
41
+ spiral/core/table/manifests/__init__.pyi,sha256=3V59-K1qr1z2dGfgRKXaHSVheK8NNw8Q8PFhfbeQd_4,1065
42
+ spiral/core/table/metastore/__init__.pyi,sha256=dMqySDnsjPUTBuFU2MaQGyocKEoGkWpeTQmUP2iIKbc,1880
43
+ spiral/core/table/spec/__init__.pyi,sha256=D4GQp9RWwyLKTlRW7eDXcQE-xA5rF2iBcXZ8y7b48EE,5595
44
+ spiral/datetime_.py,sha256=1TA1RYIRU22qcUuipIjVhAtGnPDVn2z9WttuhkmfkwY,964
45
+ spiral/expressions/__init__.py,sha256=QAEtghxWoT-obmMsHpWwPloRh5EZFpeQ7H_i9F4PG5c,6556
46
+ spiral/expressions/base.py,sha256=q_W9XslcdFQtOIE_d1VkEmLickaXKOAoIcFeMoh-nqQ,4751
47
+ spiral/expressions/http.py,sha256=begUydWoFHEqjeLkATvI_v66Ez6_rR-OQBWO5cHbb9c,2742
48
+ spiral/expressions/io.py,sha256=gJ2a0FKMmdxarWKENulPRwH7KDvSJTIh_OUxX306xAM,3045
49
+ spiral/expressions/list_.py,sha256=MMt5lf5H1M3O-x6N_PvqOLGq9NOk6Ukv0fPWwPC_uy4,1809
50
+ spiral/expressions/mp4.py,sha256=R-fcVYRI6KaH1Nwpmqnsc1VYd9wA7Nuiy2UDcNxEzpw,2165
51
+ spiral/expressions/png.py,sha256=KO8X0OmMzUFwpg2I_j0JTyldPzVXDWIMzjWMWDV9vIY,506
52
+ spiral/expressions/qoi.py,sha256=gvIbb6fXb_Bb080sn9wkpbGGrPs2UEcTXCfuv4-kcYQ,506
53
+ spiral/expressions/refs.py,sha256=ISMtJtUL--BjHF6rsvgN3Um4QcvVqQE9URngOxjQrhw,2115
54
+ spiral/expressions/str_.py,sha256=tY8RXW3JWvr1-bEfCZtk5FAf11wKJnXPuA9EoeJ9tA4,1265
55
+ spiral/expressions/struct.py,sha256=pGAnCDh6AK0BK1XfZ1qG4ce4ranIQEE1HQsgmzBcfwQ,2038
56
+ spiral/expressions/text.py,sha256=-02gBWYoyNQ3qQ1--9HTa8IryUDojYQVIp8C7rgnOWQ,1893
57
+ spiral/expressions/tiff.py,sha256=fQwIn0kLFBM2Y3YYIHmTgb_EIRHKT2fNc77nioDQQw4,8044
58
+ spiral/expressions/udf.py,sha256=r6398z2Aj7KnXtwEvCiGNbgOXa6xsb_bnnG-FEvFxV4,1370
59
+ spiral/grpc_.py,sha256=f3czdP1Mxme42Y5--a5ogYq1TTiWn-J_MlGjwJ2mWwM,1015
60
+ spiral/iceberg/__init__.py,sha256=jSIlTxWauAbJV5gsWglZisFbnfNNzLYN90scoYcdWzc,65
61
+ spiral/iceberg/client.py,sha256=E6FyE_h2HLgDW1cAFg1XgglJr6rbVOCWjRtRmqoMVkM,1003
62
+ spiral/indexes/__init__.py,sha256=TXLQ-_3xso3lFIp2lM58_ip9OPNwPKFv1FdsWiUF-d8,178
63
+ spiral/indexes/client.py,sha256=NsFBILEHMjyCUruFrUEKucRQRrN4OvqgbL4pmzWs07g,5600
64
+ spiral/indexes/index.py,sha256=4CmSFlZYp46B2CjqtiyZ7VF5EH3duiutz3nWFnyApLA,973
65
+ spiral/indexes/scan.py,sha256=B2m-UgNuawNB90HXK33GTQfMy2WLdNNxiiB6cIjFW2Y,697
66
+ spiral/project.py,sha256=0uJ1Jb88Ie-cCNnSdX3QfFtCUqrjLka4zCm_TxCpVak,1189
67
+ spiral/protogen/_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
+ spiral/protogen/_/arrow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
+ spiral/protogen/_/arrow/flight/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
+ spiral/protogen/_/arrow/flight/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ spiral/protogen/_/arrow/flight/protocol/sql/__init__.py,sha256=_xhj9QkWEW1qZ-iVxcQ8k4EjYr7KJ5ofitJGqVUGQi4,79921
72
+ spiral/protogen/_/scandal/__init__.py,sha256=X5YJqErZDIXxTESw8fLqJp3P2wZlqAglBzPs3LpTd-w,5145
73
+ spiral/protogen/_/spiral/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
+ spiral/protogen/_/spiral/table/__init__.py,sha256=o_aNyTuJBIRY6MlAWceMsjbfaSUuZphRxiG_IXmC0mU,629
75
+ spiral/protogen/_/substrait/__init__.py,sha256=pV4-T-lwAHKkfFrNYSUGY4IkbIvuKjSo_imzF7BLj_s,126526
76
+ spiral/protogen/_/substrait/extensions/__init__.py,sha256=yD7dg0TBqn-GK_L0qeVof1GKnwSLg_kPyQSV3kcSljs,3655
77
+ spiral/protogen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ spiral/protogen/substrait/__init__.py,sha256=pV4-T-lwAHKkfFrNYSUGY4IkbIvuKjSo_imzF7BLj_s,126526
79
+ spiral/protogen/substrait/extensions/__init__.py,sha256=yD7dg0TBqn-GK_L0qeVof1GKnwSLg_kPyQSV3kcSljs,3655
80
+ spiral/protogen/util.py,sha256=smnvVo6nYH3FfDm9jqhNLaXz4bbTBaQezHQDCTvZyiQ,1486
81
+ spiral/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
+ spiral/server.py,sha256=ztBmB5lBnUz-smQxR_tC8AI5SOhz17wH0MI3GuzDUdM,600
83
+ spiral/settings.py,sha256=PIQV2ljtB3pEOWoMRVSRzSGJNrXviO2JBgZ5ZY_Nq2E,2794
84
+ spiral/substrait_.py,sha256=RNSmfbGFT_5uyo8AFtzS9A7IHW3DkacMTw2vKnj0Das,12762
85
+ spiral/tables/__init__.py,sha256=iiP7BkHA117em37_e75jtdvoZC10xCXtld18gRnPbTw,430
86
+ spiral/tables/client.py,sha256=l_wJJRf3BPD5lg4Q1Ll2lAqQIuBCnKwC6JtsAui91Tc,4915
87
+ spiral/tables/dataset.py,sha256=DuHeKVCJfXLsbxmde9QW6yvesW5uhswG6qAxV5X0ZgA,7890
88
+ spiral/tables/debug/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
+ spiral/tables/debug/manifests.py,sha256=E_-DiMBg2EPL97cl9hLWhiqEsFtjEBgh_C7jZy8EWYc,2594
90
+ spiral/tables/debug/metrics.py,sha256=XdRDcjggtsLNGCAjam6IxG9072pz_d2C8iLApNRFUtk,2044
91
+ spiral/tables/debug/scan.py,sha256=-IWX_UjO4QP9Hj7PtZ1rLlbswJcryOin56GT-exqFm4,8942
92
+ spiral/tables/maintenance.py,sha256=7Xa2Jdu_OY1Qu6iN1sPVdywVZtk_Mv3EaC3G93cmQvI,305
93
+ spiral/tables/scan.py,sha256=3lPf5fSyF1fHGdGJ-pvu5HxPWoonf_XL7neWTqzB-0I,7582
94
+ spiral/tables/snapshot.py,sha256=2NTuVEp2uJ1pV3Q5tLj7FOzPSc9axlfb6uOITwHnj0g,2229
95
+ spiral/tables/table.py,sha256=4B2drwwfaoL6aIJ-5Ll-Bqza-EBeDIfMkuszSOZqSpk,5326
96
+ spiral/tables/transaction.py,sha256=3a64R-mf_cmR54BNn8U-05jmWonp6Ivxhe6u01Dyjzo,1573
97
+ spiral/types_.py,sha256=W_jyO7F6rpPiH69jhgSgV7OxQZbOlb1Ho3InpKUP6Eo,155
98
+ pyspiral-0.4.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.8.3)
2
+ Generator: maturin (1.9.3)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp310-abi3-macosx_11_0_arm64
spiral/__init__.py CHANGED
@@ -1,13 +1,10 @@
1
- """Python client for the Spiral warehouse."""
1
+ """Python client for Spiral"""
2
2
 
3
+ # This is here to make sure we load the native extension first
3
4
  from spiral import _lib
4
- from spiral.catalog import Spiral
5
- from spiral.maintenance import Maintenance
6
- from spiral.scan_ import Scan, scan
7
- from spiral.table import Table
8
- from spiral.txn import Transaction
9
5
 
10
- # Eagerly import the Spiral library
11
- assert _lib, "Spiral library"
6
+ assert _lib
12
7
 
13
- __all__ = ["scan", "Scan", "Table", "Spiral", "Transaction", "Maintenance"]
8
+ from spiral.client import Spiral # noqa: E402, I001
9
+
10
+ __all__ = ["Spiral"]
spiral/_lib.abi3.so CHANGED
Binary file
spiral/adbc.py CHANGED
@@ -4,7 +4,6 @@ import logging
4
4
  from concurrent.futures import ThreadPoolExecutor
5
5
  from urllib.parse import urlparse
6
6
 
7
- import duckdb
8
7
  import pyarrow as pa
9
8
  import pyarrow.compute as pc
10
9
  import sqlglot
@@ -25,8 +24,9 @@ from pyarrow.flight import (
25
24
  )
26
25
 
27
26
  from spiral import Spiral
28
- from spiral.proto._.arrow.flight.protocol import sql as rpc
29
- from spiral.proto._.arrow.flight.protocol.sql import (
27
+ from spiral.api.projects import TableResource
28
+ from spiral.protogen._.arrow.flight.protocol import sql as rpc
29
+ from spiral.protogen._.arrow.flight.protocol.sql import (
30
30
  CommandGetCatalogs,
31
31
  CommandGetDbSchemas,
32
32
  CommandGetSqlInfo,
@@ -37,6 +37,7 @@ from spiral.proto._.arrow.flight.protocol.sql import (
37
37
  )
38
38
 
39
39
  log = logging.getLogger(__name__)
40
+ logging.getLogger("sqlx").setLevel(logging.WARNING)
40
41
 
41
42
 
42
43
  def debuggable(func):
@@ -63,6 +64,7 @@ def debuggable(func):
63
64
  return wrapper_decorator
64
65
 
65
66
 
67
+ # TODO(marko): This should work for Iceberg tables.
66
68
  class ADBCServerBase:
67
69
  def get_sql_info(self, _req: CommandGetSqlInfo) -> pa.RecordBatchReader:
68
70
  """Default implementation that reports no support for any complex features."""
@@ -71,7 +73,7 @@ class ADBCServerBase:
71
73
  SqlInfo.FLIGHT_SQL_SERVER_VERSION: "0.0.1",
72
74
  SqlInfo.FLIGHT_SQL_SERVER_ARROW_VERSION: pa.__version__,
73
75
  SqlInfo.FLIGHT_SQL_SERVER_READ_ONLY: True,
74
- SqlInfo.FLIGHT_SQL_SERVER_TRANSACTION: SqlSupportedTransaction.SQL_SUPPORTED_TRANSACTION_NONE.value,
76
+ SqlInfo.FLIGHT_SQL_SERVER_TRANSACTION: SqlSupportedTransaction.NONE.value,
75
77
  }
76
78
 
77
79
  # See https://github.com/apache/arrow-adbc/blob/38c21c2311a59803559cb0091b3f34180c28b25f/rust/core/src/schemas.rs#L35
@@ -147,7 +149,7 @@ class SpiralADBCServer(ADBCServerBase):
147
149
  @debuggable
148
150
  def batches():
149
151
  yield pa.RecordBatch.from_arrays(
150
- [list(self.sp.list_project_ids())],
152
+ [[p.id for p in self.sp.list_projects()]],
151
153
  schema=schema,
152
154
  )
153
155
 
@@ -171,12 +173,13 @@ class SpiralADBCServer(ADBCServerBase):
171
173
 
172
174
  # Otherwise, catalog is either the project ID, or None.
173
175
  if req.catalog is None:
174
- projects = list(self.sp.list_projects())
176
+ projects = self.sp.list_projects()
175
177
  else:
176
178
  projects = [self.sp.project(req.catalog)]
177
179
 
178
180
  for project in projects:
179
- datasets = {dt[0] for dt in project.list_table_names()}
181
+ datasets = {dt.dataset for dt in project.tables.list_tables()}
182
+
180
183
  batch = pa.RecordBatch.from_arrays(
181
184
  [
182
185
  [project.id] * len(datasets),
@@ -218,21 +221,20 @@ class SpiralADBCServer(ADBCServerBase):
218
221
  projects = [self.sp.project(req.catalog)]
219
222
 
220
223
  def _process_project(project):
221
- tables = project.list_tables()
224
+ tables: list[TableResource] = project.tables.list_tables()
222
225
 
223
226
  rows = []
224
227
  for table in tables:
225
- _project_id, dataset, name = str(table).split(".")
226
-
227
228
  row = {
228
229
  "catalog_name": project.id,
229
- "db_schema_name": dataset,
230
- "table_name": name,
230
+ "db_schema_name": table.dataset,
231
+ "table_name": table.table,
231
232
  "table_type": "TABLE",
232
233
  }
233
234
 
234
235
  if req.include_schema:
235
- row["table_schema"] = table.to_dataset().schema.serialize().to_pybytes()
236
+ open_table = project.tables.table(f"{table.project_id}.{table.dataset}.{table.table}")
237
+ row["table_schema"] = open_table.snapshot().to_dataset().schema.serialize().to_pybytes()
236
238
 
237
239
  rows.append(row)
238
240
 
@@ -251,9 +253,14 @@ class SpiralADBCServer(ADBCServerBase):
251
253
  # This lets us insert a PyArrow Dataset into Python locals such that
252
254
  # DuckDB will pick up on it for the query.
253
255
  name = exp.table_name(tbl)
254
- locals()[name] = self.sp.project(tbl.catalog).table(f"{tbl.db}.{tbl.name}").to_dataset()
256
+ locals()[name] = self.sp.tables.table(f"{tbl.catalog}.{tbl.db}.{tbl.name}").snapshot().to_dataset()
255
257
  tbl.replace(exp.table_(table=name))
256
258
 
259
+ try:
260
+ import duckdb
261
+ except ImportError:
262
+ raise FlightError("DuckDB is required for SQL queries.")
263
+
257
264
  try:
258
265
  sql = duckdb.sql(expr.sql(dialect="duckdb"))
259
266
  except Exception as e:
spiral/api/__init__.py CHANGED
@@ -1,163 +1,23 @@
1
- import abc
2
- import base64
3
- import logging
4
1
  import os
5
- import re
6
- import socket
7
- import time
8
- from collections.abc import Iterable, Iterator
9
- from typing import TYPE_CHECKING, Annotated, Generic, TypeVar
2
+ from typing import TYPE_CHECKING
10
3
 
11
- import betterproto
12
4
  import httpx
13
- import pyarrow as pa
14
- from httpx import HTTPStatusError
15
- from pydantic import (
16
- BaseModel,
17
- BeforeValidator,
18
- Field,
19
- GetPydanticSchema,
20
- PlainSerializer,
21
- StringConstraints,
22
- TypeAdapter,
23
- )
5
+
6
+ from .client import _Client
24
7
 
25
8
  if TYPE_CHECKING:
9
+ from spiral.core.client import Authn
10
+
26
11
  from .admin import AdminService
27
12
  from .filesystems import FileSystemService
28
13
  from .organizations import OrganizationService
29
14
  from .projects import ProjectService
30
- from .tables import TableService
31
- from .tokens import TokenService
15
+ from .telemetry import TelemetryService
32
16
  from .workloads import WorkloadService
33
17
 
34
- log = logging.getLogger(__name__)
35
-
36
- RE_ID = re.compile("[a-zA-Z0-9-]+")
37
-
38
- OrganizationId = Annotated[str, StringConstraints(min_length=1, max_length=64)] # , pattern=RE_ID)]
39
- ProjectId = Annotated[str, StringConstraints(min_length=1, max_length=64)] # , pattern=RE_ID)]
40
- RoleId = str
41
-
42
- #: Annotations to implement pa.Schema serde with byte arrays
43
- ArrowSchema = Annotated[
44
- pa.Schema,
45
- GetPydanticSchema(lambda tp, handler: handler(object)),
46
- BeforeValidator(
47
- lambda v: v
48
- if isinstance(v, pa.Schema)
49
- else pa.ipc.read_schema(pa.ipc.read_message(base64.urlsafe_b64decode(v)))
50
- ),
51
- PlainSerializer(lambda schema: base64.urlsafe_b64encode(schema.serialize().to_pybytes())),
52
- ]
53
-
54
- E = TypeVar("E")
55
-
56
-
57
- class PagedRequest(BaseModel):
58
- page_token: str | None = None
59
- page_size: int = 50
60
-
61
-
62
- class PagedResponse(BaseModel, Generic[E]):
63
- items: list[E] = Field(default_factory=list)
64
- next_page_token: str | None = None
65
-
66
-
67
- PagedReqT = TypeVar("PagedReqT", bound=PagedRequest)
68
-
69
-
70
- class Paged(Iterable[E], Generic[E]):
71
- def __init__(
72
- self,
73
- client: "_Client",
74
- path: str,
75
- request: PagedRequest,
76
- response_cls: type[PagedResponse[E]],
77
- ):
78
- self._client = client
79
- self._path = path
80
- self._request: PagedRequest = request
81
- self._response_cls = response_cls
82
-
83
- self._response: PagedResponse[E] = client.put(path, request, response_cls)
84
-
85
- @property
86
- def page(self) -> PagedResponse[E]:
87
- return self._response
88
-
89
- def __iter__(self) -> Iterator[E]:
90
- while True:
91
- yield from self._response.items
92
-
93
- if self._response.next_page_token is None:
94
- break
95
-
96
- self._request = self._request.model_copy(update=dict(page_token=self._response.next_page_token))
97
- self._response = self._client.put(self._path, self._request, self._response_cls)
98
-
99
-
100
- class ServiceBase:
101
- def __init__(self, client: "_Client"):
102
- self.client = client
103
-
104
-
105
- class Authn:
106
- """An abstract class for credential providers."""
107
-
108
- @abc.abstractmethod
109
- def token(self) -> str | None:
110
- """Return a token, if available."""
111
-
112
-
113
- class _Client:
114
- RequestT = TypeVar("RequestT")
115
- ResponseT = TypeVar("ResponseT")
116
-
117
- def __init__(self, http: httpx.Client, authn: Authn):
118
- self.http = http
119
- self.authn = authn
120
-
121
- def get(self, path: str, response_cls: type[ResponseT]) -> ResponseT:
122
- return self.request("GET", path, None, response_cls)
123
-
124
- def post(self, path: str, req: RequestT, response_cls: type[ResponseT]) -> ResponseT:
125
- return self.request("POST", path, req, response_cls)
126
-
127
- def put(self, path: str, req: RequestT, response_cls: type[ResponseT]) -> ResponseT:
128
- return self.request("PUT", path, req, response_cls)
129
-
130
- def request(self, method: str, path: str, req: RequestT | None, response_cls: type[ResponseT]) -> ResponseT:
131
- if isinstance(req, betterproto.Message):
132
- try:
133
- req = dict(content=bytes(req))
134
- except:
135
- raise
136
- elif req is not None:
137
- req = dict(json=TypeAdapter(req.__class__).dump_python(req, mode="json") if req is not None else None)
138
- else:
139
- req = dict()
140
-
141
- token = self.authn.token()
142
- resp = self.http.request(method, path, headers={"authorization": f"Bearer {token}" if token else None}, **req)
143
-
144
- try:
145
- resp.raise_for_status()
146
- except HTTPStatusError as e:
147
- # Enrich the exception with the response body
148
- raise HTTPStatusError(f"{str(e)}: {resp.text}", request=e.request, response=e.response)
149
-
150
- if issubclass(response_cls, betterproto.Message):
151
- return response_cls().parse(resp.content)
152
- else:
153
- return TypeAdapter(response_cls).validate_python(resp.json())
154
-
155
- def paged(self, path: str, req: PagedRequest, response_cls: type[PagedResponse[E]]) -> Paged[E]:
156
- return Paged(self, path, req, response_cls)
157
-
158
18
 
159
19
  class SpiralAPI:
160
- def __init__(self, authn: Authn, base_url: str | None = None):
20
+ def __init__(self, authn: "Authn", base_url: str | None = None):
161
21
  self.base_url = base_url or os.environ.get("SPIRAL_URL", "https://api.spiraldb.com")
162
22
  self.client = _Client(
163
23
  httpx.Client(
@@ -173,24 +33,12 @@ class SpiralAPI:
173
33
 
174
34
  return AdminService(self.client)
175
35
 
176
- @property
177
- def file_system(self) -> "FileSystemService":
178
- from .filesystems import FileSystemService
179
-
180
- return FileSystemService(self.client)
181
-
182
36
  @property
183
37
  def organization(self) -> "OrganizationService":
184
38
  from .organizations import OrganizationService
185
39
 
186
40
  return OrganizationService(self.client)
187
41
 
188
- @property
189
- def token(self) -> "TokenService":
190
- from .tokens import TokenService
191
-
192
- return TokenService(self.client)
193
-
194
42
  @property
195
43
  def project(self) -> "ProjectService":
196
44
  from .projects import ProjectService
@@ -198,10 +46,10 @@ class SpiralAPI:
198
46
  return ProjectService(self.client)
199
47
 
200
48
  @property
201
- def table(self) -> "TableService":
202
- from .tables import TableService
49
+ def file_system(self) -> "FileSystemService":
50
+ from .filesystems import FileSystemService
203
51
 
204
- return TableService(self.client)
52
+ return FileSystemService(self.client)
205
53
 
206
54
  @property
207
55
  def workload(self) -> "WorkloadService":
@@ -209,17 +57,8 @@ class SpiralAPI:
209
57
 
210
58
  return WorkloadService(self.client)
211
59
 
60
+ @property
61
+ def telemetry(self) -> "TelemetryService":
62
+ from .telemetry import TelemetryService
212
63
 
213
- def wait_for_port(port: int, host: str = "localhost", timeout: float = 5.0):
214
- """Wait until a port starts accepting TCP connections."""
215
- start_time = time.time()
216
- while True:
217
- try:
218
- with socket.create_connection((host, port), timeout=timeout):
219
- break
220
- except OSError as ex:
221
- time.sleep(0.01)
222
- if time.time() - start_time >= timeout:
223
- raise TimeoutError(
224
- f"Waited too long for the port {port} on host {host} to start accepting " "connections."
225
- ) from ex
64
+ return TelemetryService(self.client)
spiral/api/admin.py CHANGED
@@ -1,29 +1,15 @@
1
- from pydantic import BaseModel
2
-
3
- from . import OrganizationId, Paged, PagedRequest, PagedResponse, ServiceBase
4
-
5
-
6
- class SyncOrgs:
7
- class Request(PagedRequest): ...
8
-
9
- class Response(PagedResponse[OrganizationId]): ...
10
-
11
-
12
- class Membership(BaseModel):
13
- user_id: str
14
- organization_id: str
15
-
16
-
17
- class SyncMemberships:
18
- class Request(PagedRequest):
19
- organization_id: OrganizationId | None = None
20
-
21
- class Response(PagedResponse[Membership]): ...
1
+ from .client import Paged, PagedResponse, ServiceBase
2
+ from .organizations import OrgMembership
3
+ from .types import OrgId
22
4
 
23
5
 
24
6
  class AdminService(ServiceBase):
25
- def sync_orgs(self, request: SyncOrgs.Request) -> Paged[SyncOrgs.Response]:
26
- return self.client.paged("/admin/sync-orgs", request, SyncOrgs.Response)
27
-
28
- def sync_memberships(self, request: SyncMemberships.Request) -> Paged[Membership]:
29
- return self.client.paged("/admin/sync-memberships", request, SyncMemberships.Response)
7
+ def sync_memberships(self, org_id: OrgId | None = None) -> Paged[OrgMembership]:
8
+ params = {}
9
+ if org_id:
10
+ params["org_id"] = str(org_id)
11
+ return self.client.paged("/v1/admin/sync-memberships", PagedResponse[OrgMembership], params=params)
12
+
13
+ def sync_orgs(self) -> Paged[OrgId]:
14
+ params = {}
15
+ return self.client.paged("/v1/admin/sync-orgs", PagedResponse[OrgId], params=params)