sverify 0.2.0__tar.gz

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 (262) hide show
  1. sverify-0.2.0/.gitignore +14 -0
  2. sverify-0.2.0/.python-version +1 -0
  3. sverify-0.2.0/PKG-INFO +241 -0
  4. sverify-0.2.0/README.md +207 -0
  5. sverify-0.2.0/great-docs.yml +134 -0
  6. sverify-0.2.0/hatch_build.py +60 -0
  7. sverify-0.2.0/lean/Main.lean +188 -0
  8. sverify-0.2.0/lean/SubstraitLean/Aggregate.lean +214 -0
  9. sverify-0.2.0/lean/SubstraitLean/Check.lean +53 -0
  10. sverify-0.2.0/lean/SubstraitLean/Completeness.lean +89 -0
  11. sverify-0.2.0/lean/SubstraitLean/Compose.lean +345 -0
  12. sverify-0.2.0/lean/SubstraitLean/ContractCheck.lean +237 -0
  13. sverify-0.2.0/lean/SubstraitLean/Contracts.lean +3070 -0
  14. sverify-0.2.0/lean/SubstraitLean/Examples.lean +202 -0
  15. sverify-0.2.0/lean/SubstraitLean/Expr.lean +253 -0
  16. sverify-0.2.0/lean/SubstraitLean/Float.lean +160 -0
  17. sverify-0.2.0/lean/SubstraitLean/Guards.lean +468 -0
  18. sverify-0.2.0/lean/SubstraitLean/Incremental.lean +295 -0
  19. sverify-0.2.0/lean/SubstraitLean/Keys.lean +824 -0
  20. sverify-0.2.0/lean/SubstraitLean/Order.lean +292 -0
  21. sverify-0.2.0/lean/SubstraitLean/Parse.lean +1862 -0
  22. sverify-0.2.0/lean/SubstraitLean/Partition.lean +1717 -0
  23. sverify-0.2.0/lean/SubstraitLean/Pretty.lean +106 -0
  24. sverify-0.2.0/lean/SubstraitLean/Rel.lean +274 -0
  25. sverify-0.2.0/lean/SubstraitLean/Rewrites.lean +93 -0
  26. sverify-0.2.0/lean/SubstraitLean/Rounding.lean +219 -0
  27. sverify-0.2.0/lean/SubstraitLean/Safety.lean +765 -0
  28. sverify-0.2.0/lean/SubstraitLean/Semantic.lean +1898 -0
  29. sverify-0.2.0/lean/SubstraitLean/ShardCheck.lean +126 -0
  30. sverify-0.2.0/lean/SubstraitLean/TableFormat.lean +400 -0
  31. sverify-0.2.0/lean/SubstraitLean/Terms.lean +99 -0
  32. sverify-0.2.0/lean/SubstraitLean/Text.lean +211 -0
  33. sverify-0.2.0/lean/SubstraitLean/Time.lean +501 -0
  34. sverify-0.2.0/lean/SubstraitLean/Value.lean +62 -0
  35. sverify-0.2.0/lean/SubstraitLean/Weighted.lean +335 -0
  36. sverify-0.2.0/lean/SubstraitLean/Window.lean +324 -0
  37. sverify-0.2.0/lean/SubstraitLean/Write.lean +703 -0
  38. sverify-0.2.0/lean/SubstraitLean.lean +29 -0
  39. sverify-0.2.0/lean/lake-manifest.json +6 -0
  40. sverify-0.2.0/lean/lakefile.toml +9 -0
  41. sverify-0.2.0/lean/lean-toolchain +1 -0
  42. sverify-0.2.0/pyproject.toml +199 -0
  43. sverify-0.2.0/scripts/bench_optimizations.py +153 -0
  44. sverify-0.2.0/scripts/capture_fixtures.py +171 -0
  45. sverify-0.2.0/scripts/compare_sql_producers.py +60 -0
  46. sverify-0.2.0/scripts/fetch_isthmus.sh +25 -0
  47. sverify-0.2.0/scripts/isthmus_catalogue.py +159 -0
  48. sverify-0.2.0/scripts/publish_notebooks.py +118 -0
  49. sverify-0.2.0/src/sverify/__init__.py +56 -0
  50. sverify-0.2.0/src/sverify/asof.py +79 -0
  51. sverify-0.2.0/src/sverify/catalogue.py +831 -0
  52. sverify-0.2.0/src/sverify/contracts.py +1355 -0
  53. sverify-0.2.0/src/sverify/counterexample.py +249 -0
  54. sverify-0.2.0/src/sverify/coverage.py +214 -0
  55. sverify-0.2.0/src/sverify/coverage_cases/__init__.py +66 -0
  56. sverify-0.2.0/src/sverify/coverage_cases/_common.py +104 -0
  57. sverify-0.2.0/src/sverify/coverage_cases/aggregation.py +78 -0
  58. sverify-0.2.0/src/sverify/coverage_cases/joins.py +99 -0
  59. sverify-0.2.0/src/sverify/coverage_cases/lists.py +36 -0
  60. sverify-0.2.0/src/sverify/coverage_cases/nulls.py +18 -0
  61. sverify-0.2.0/src/sverify/coverage_cases/numbers.py +29 -0
  62. sverify-0.2.0/src/sverify/coverage_cases/rows.py +65 -0
  63. sverify-0.2.0/src/sverify/coverage_cases/strings.py +22 -0
  64. sverify-0.2.0/src/sverify/coverage_cases/temporal.py +177 -0
  65. sverify-0.2.0/src/sverify/coverage_cases/windows.py +160 -0
  66. sverify-0.2.0/src/sverify/display.py +91 -0
  67. sverify-0.2.0/src/sverify/duckdb_engine.py +59 -0
  68. sverify-0.2.0/src/sverify/engine.py +839 -0
  69. sverify-0.2.0/src/sverify/engines.py +96 -0
  70. sverify-0.2.0/src/sverify/errors.py +35 -0
  71. sverify-0.2.0/src/sverify/examples.py +57 -0
  72. sverify-0.2.0/src/sverify/fuzz.py +631 -0
  73. sverify-0.2.0/src/sverify/ibis_substrait/LICENSE +201 -0
  74. sverify-0.2.0/src/sverify/ibis_substrait/README.md +10 -0
  75. sverify-0.2.0/src/sverify/ibis_substrait/__init__.py +1612 -0
  76. sverify-0.2.0/src/sverify/incremental.py +1283 -0
  77. sverify-0.2.0/src/sverify/io/__init__.py +55 -0
  78. sverify-0.2.0/src/sverify/io/_common.py +374 -0
  79. sverify-0.2.0/src/sverify/io/delta.py +631 -0
  80. sverify-0.2.0/src/sverify/io/iceberg.py +651 -0
  81. sverify-0.2.0/src/sverify/nodes.py +761 -0
  82. sverify-0.2.0/src/sverify/partition.py +312 -0
  83. sverify-0.2.0/src/sverify/polars_engine.py +1942 -0
  84. sverify-0.2.0/src/sverify/py.typed +0 -0
  85. sverify-0.2.0/src/sverify/skills/sverify/SKILL.md +141 -0
  86. sverify-0.2.0/src/sverify/skills/sverify/references/contracts.md +78 -0
  87. sverify-0.2.0/src/sverify/skills/sverify-engines/SKILL.md +138 -0
  88. sverify-0.2.0/src/sverify/skills/sverify-engines/references/semantics.md +30 -0
  89. sverify-0.2.0/src/sverify/skills/sverify-pipelines/SKILL.md +140 -0
  90. sverify-0.2.0/src/sverify/skills/sverify-pipelines/references/table-formats.md +116 -0
  91. sverify-0.2.0/src/sverify/spark_engine.py +124 -0
  92. sverify-0.2.0/src/sverify/sql/__init__.py +9 -0
  93. sverify-0.2.0/src/sverify/sql/core.py +3594 -0
  94. sverify-0.2.0/src/sverify/sql/duckdb.py +162 -0
  95. sverify-0.2.0/src/sverify/sql/snowflake.py +326 -0
  96. sverify-0.2.0/src/sverify/sql/spark.py +322 -0
  97. sverify-0.2.0/src/sverify/sql_model.py +89 -0
  98. sverify-0.2.0/src/sverify/testing.py +172 -0
  99. sverify-0.2.0/tests/conftest.py +118 -0
  100. sverify-0.2.0/tests/data/delta_deletion_vectors/_delta_log/00000000000000000000.json +3 -0
  101. sverify-0.2.0/tests/data/delta_deletion_vectors/_delta_log/00000000000000000001.json +2 -0
  102. sverify-0.2.0/tests/data/delta_deletion_vectors/_delta_log/00000000000000000002.json +3 -0
  103. sverify-0.2.0/tests/data/delta_deletion_vectors/deletion_vector_0045963d-73fc-48ff-ab94-2ecfbf35731a.bin +0 -0
  104. sverify-0.2.0/tests/data/delta_deletion_vectors/part-00000-a3785255-ffa6-42d4-b755-8b98ea6da28b-c000.snappy.parquet +0 -0
  105. sverify-0.2.0/tests/spark_session.py +46 -0
  106. sverify-0.2.0/tests/sql_snapshots/df_aggregate.snowflake.sql +1 -0
  107. sverify-0.2.0/tests/sql_snapshots/df_aggregate.spark.sql +1 -0
  108. sverify-0.2.0/tests/sql_snapshots/df_anti_join.snowflake.sql +1 -0
  109. sverify-0.2.0/tests/sql_snapshots/df_anti_join.spark.sql +1 -0
  110. sverify-0.2.0/tests/sql_snapshots/df_asof.snowflake.sql +1 -0
  111. sverify-0.2.0/tests/sql_snapshots/df_asof.spark.sql +1 -0
  112. sverify-0.2.0/tests/sql_snapshots/df_distinct.snowflake.sql +1 -0
  113. sverify-0.2.0/tests/sql_snapshots/df_distinct.spark.sql +1 -0
  114. sverify-0.2.0/tests/sql_snapshots/df_filter_project.snowflake.sql +1 -0
  115. sverify-0.2.0/tests/sql_snapshots/df_filter_project.spark.sql +1 -0
  116. sverify-0.2.0/tests/sql_snapshots/df_float_timestamp.duckdb.sql +1 -0
  117. sverify-0.2.0/tests/sql_snapshots/df_float_timestamp.snowflake.sql +1 -0
  118. sverify-0.2.0/tests/sql_snapshots/df_float_timestamp.spark.sql +1 -0
  119. sverify-0.2.0/tests/sql_snapshots/df_inner_join.snowflake.sql +1 -0
  120. sverify-0.2.0/tests/sql_snapshots/df_inner_join.spark.sql +1 -0
  121. sverify-0.2.0/tests/sql_snapshots/df_order_by.snowflake.sql +1 -0
  122. sverify-0.2.0/tests/sql_snapshots/df_order_by.spark.sql +1 -0
  123. sverify-0.2.0/tests/sql_snapshots/df_order_by_limit.snowflake.sql +1 -0
  124. sverify-0.2.0/tests/sql_snapshots/df_order_by_limit.spark.sql +1 -0
  125. sverify-0.2.0/tests/sql_snapshots/df_outer_join.snowflake.sql +1 -0
  126. sverify-0.2.0/tests/sql_snapshots/df_outer_join.spark.sql +1 -0
  127. sverify-0.2.0/tests/sql_snapshots/df_self_join.snowflake.sql +1 -0
  128. sverify-0.2.0/tests/sql_snapshots/df_self_join.spark.sql +1 -0
  129. sverify-0.2.0/tests/sql_snapshots/df_semi_join.snowflake.sql +1 -0
  130. sverify-0.2.0/tests/sql_snapshots/df_semi_join.spark.sql +1 -0
  131. sverify-0.2.0/tests/sql_snapshots/df_strings.snowflake.sql +1 -0
  132. sverify-0.2.0/tests/sql_snapshots/df_strings.spark.sql +1 -0
  133. sverify-0.2.0/tests/sql_snapshots/df_time.snowflake.sql +1 -0
  134. sverify-0.2.0/tests/sql_snapshots/df_time.spark.sql +1 -0
  135. sverify-0.2.0/tests/sql_snapshots/df_union_all.snowflake.sql +1 -0
  136. sverify-0.2.0/tests/sql_snapshots/df_union_all.spark.sql +1 -0
  137. sverify-0.2.0/tests/sql_snapshots/df_window.duckdb.sql +1 -0
  138. sverify-0.2.0/tests/sql_snapshots/df_window.snowflake.sql +1 -0
  139. sverify-0.2.0/tests/sql_snapshots/df_window.spark.sql +1 -0
  140. sverify-0.2.0/tests/sql_snapshots/filter_project.duckdb.sql +1 -0
  141. sverify-0.2.0/tests/sql_snapshots/filter_project.snowflake.sql +1 -0
  142. sverify-0.2.0/tests/sql_snapshots/filter_project.spark.sql +1 -0
  143. sverify-0.2.0/tests/sql_snapshots/group_by_without_measures.snowflake.sql +1 -0
  144. sverify-0.2.0/tests/sql_snapshots/group_by_without_measures.spark.sql +1 -0
  145. sverify-0.2.0/tests/sql_snapshots/isthmus_char_literals.snowflake.sql +1 -0
  146. sverify-0.2.0/tests/sql_snapshots/isthmus_char_literals.spark.sql +1 -0
  147. sverify-0.2.0/tests/sql_snapshots/isthmus_compare_int_double.snowflake.sql +1 -0
  148. sverify-0.2.0/tests/sql_snapshots/isthmus_compare_int_double.spark.sql +1 -0
  149. sverify-0.2.0/tests/sql_snapshots/isthmus_date_compare.snowflake.sql +1 -0
  150. sverify-0.2.0/tests/sql_snapshots/isthmus_date_compare.spark.sql +1 -0
  151. sverify-0.2.0/tests/sql_snapshots/isthmus_date_iday.snowflake.sql +1 -0
  152. sverify-0.2.0/tests/sql_snapshots/isthmus_date_iday.spark.sql +1 -0
  153. sverify-0.2.0/tests/sql_snapshots/isthmus_is_true.snowflake.sql +1 -0
  154. sverify-0.2.0/tests/sql_snapshots/isthmus_is_true.spark.sql +1 -0
  155. sverify-0.2.0/tests/sql_snapshots/isthmus_nested_window.snowflake.sql +1 -0
  156. sverify-0.2.0/tests/sql_snapshots/isthmus_nested_window.spark.sql +1 -0
  157. sverify-0.2.0/tests/sql_snapshots/isthmus_power.snowflake.sql +1 -0
  158. sverify-0.2.0/tests/sql_snapshots/isthmus_power.spark.sql +1 -0
  159. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter.snowflake.sql +1 -0
  160. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter.spark.sql +1 -0
  161. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter_delta0.snowflake.sql +1 -0
  162. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter_delta0.spark.sql +1 -0
  163. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter_delta1.snowflake.sql +1 -0
  164. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter_delta1.spark.sql +1 -0
  165. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter_delta2.snowflake.sql +1 -0
  166. sverify-0.2.0/tests/sql_snapshots/pipeline_cross_filter_delta2.spark.sql +1 -0
  167. sverify-0.2.0/tests/sql_snapshots/pipeline_fetch_inlined.snowflake.sql +1 -0
  168. sverify-0.2.0/tests/sql_snapshots/pipeline_fetch_inlined.spark.sql +1 -0
  169. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute.snowflake.sql +1 -0
  170. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute.spark.sql +1 -0
  171. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute_delta0.snowflake.sql +1 -0
  172. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute_delta0.spark.sql +1 -0
  173. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute_delta1.snowflake.sql +1 -0
  174. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute_delta1.spark.sql +1 -0
  175. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute_delta2.snowflake.sql +1 -0
  176. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_join_compute_delta2.spark.sql +1 -0
  177. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_project.snowflake.sql +1 -0
  178. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_project.spark.sql +1 -0
  179. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_project_delta0.snowflake.sql +1 -0
  180. sverify-0.2.0/tests/sql_snapshots/pipeline_filter_project_delta0.spark.sql +1 -0
  181. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join.snowflake.sql +1 -0
  182. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join.spark.sql +1 -0
  183. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join_delta0.snowflake.sql +1 -0
  184. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join_delta0.spark.sql +1 -0
  185. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join_delta1.snowflake.sql +1 -0
  186. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join_delta1.spark.sql +1 -0
  187. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join_delta2.snowflake.sql +1 -0
  188. sverify-0.2.0/tests/sql_snapshots/pipeline_inner_join_delta2.spark.sql +1 -0
  189. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join.snowflake.sql +1 -0
  190. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join.spark.sql +1 -0
  191. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join_delta0.snowflake.sql +1 -0
  192. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join_delta0.spark.sql +1 -0
  193. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join_delta1.snowflake.sql +1 -0
  194. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join_delta1.spark.sql +1 -0
  195. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join_delta2.snowflake.sql +1 -0
  196. sverify-0.2.0/tests/sql_snapshots/pipeline_self_join_delta2.spark.sql +1 -0
  197. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query.snowflake.sql +1 -0
  198. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query.spark.sql +1 -0
  199. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query_delta0.snowflake.sql +1 -0
  200. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query_delta0.spark.sql +1 -0
  201. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query_delta1.snowflake.sql +1 -0
  202. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query_delta1.spark.sql +1 -0
  203. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query_delta2.snowflake.sql +1 -0
  204. sverify-0.2.0/tests/sql_snapshots/pipeline_sql_query_delta2.spark.sql +1 -0
  205. sverify-0.2.0/tests/sql_snapshots/pipeline_union_all.snowflake.sql +1 -0
  206. sverify-0.2.0/tests/sql_snapshots/pipeline_union_all.spark.sql +1 -0
  207. sverify-0.2.0/tests/sql_snapshots/pipeline_union_all_delta0.snowflake.sql +1 -0
  208. sverify-0.2.0/tests/sql_snapshots/pipeline_union_all_delta0.spark.sql +1 -0
  209. sverify-0.2.0/tests/sql_snapshots/pipeline_union_all_delta1.snowflake.sql +1 -0
  210. sverify-0.2.0/tests/sql_snapshots/pipeline_union_all_delta1.spark.sql +1 -0
  211. sverify-0.2.0/tests/sql_snapshots/pipeline_window_inlined.snowflake.sql +1 -0
  212. sverify-0.2.0/tests/sql_snapshots/pipeline_window_inlined.spark.sql +1 -0
  213. sverify-0.2.0/tests/sql_snapshots/pipeline_window_tie_free.snowflake.sql +1 -0
  214. sverify-0.2.0/tests/sql_snapshots/pipeline_window_tie_free.spark.sql +1 -0
  215. sverify-0.2.0/tests/sql_snapshots/typed_greatest.snowflake.sql +1 -0
  216. sverify-0.2.0/tests/sql_snapshots/typed_greatest.spark.sql +1 -0
  217. sverify-0.2.0/tests/sql_snapshots/union_fetch.duckdb.sql +1 -0
  218. sverify-0.2.0/tests/sql_snapshots/union_fetch.snowflake.sql +1 -0
  219. sverify-0.2.0/tests/sql_snapshots/union_fetch.spark.sql +1 -0
  220. sverify-0.2.0/tests/sql_snapshots/virtual_left_join.duckdb.sql +1 -0
  221. sverify-0.2.0/tests/sql_snapshots/virtual_left_join.snowflake.sql +1 -0
  222. sverify-0.2.0/tests/sql_snapshots/virtual_left_join.spark.sql +1 -0
  223. sverify-0.2.0/tests/test_aggregate.py +240 -0
  224. sverify-0.2.0/tests/test_asof.py +143 -0
  225. sverify-0.2.0/tests/test_backend_semantics.py +420 -0
  226. sverify-0.2.0/tests/test_backends.py +282 -0
  227. sverify-0.2.0/tests/test_catalogue.py +82 -0
  228. sverify-0.2.0/tests/test_compose.py +148 -0
  229. sverify-0.2.0/tests/test_contracts.py +250 -0
  230. sverify-0.2.0/tests/test_counterexample.py +115 -0
  231. sverify-0.2.0/tests/test_coverage.py +22 -0
  232. sverify-0.2.0/tests/test_datafusion_quirks.py +487 -0
  233. sverify-0.2.0/tests/test_floats.py +172 -0
  234. sverify-0.2.0/tests/test_fuzz.py +169 -0
  235. sverify-0.2.0/tests/test_guards.py +99 -0
  236. sverify-0.2.0/tests/test_ibis_semantics.py +125 -0
  237. sverify-0.2.0/tests/test_ibis_substrait.py +229 -0
  238. sverify-0.2.0/tests/test_incremental.py +143 -0
  239. sverify-0.2.0/tests/test_io_delta.py +804 -0
  240. sverify-0.2.0/tests/test_io_iceberg.py +390 -0
  241. sverify-0.2.0/tests/test_isthmus.py +208 -0
  242. sverify-0.2.0/tests/test_nodes.py +329 -0
  243. sverify-0.2.0/tests/test_numeric.py +243 -0
  244. sverify-0.2.0/tests/test_partition.py +267 -0
  245. sverify-0.2.0/tests/test_pipeline.py +216 -0
  246. sverify-0.2.0/tests/test_polars.py +665 -0
  247. sverify-0.2.0/tests/test_public_api.py +51 -0
  248. sverify-0.2.0/tests/test_publish_notebooks.py +88 -0
  249. sverify-0.2.0/tests/test_readme.py +65 -0
  250. sverify-0.2.0/tests/test_relations.py +127 -0
  251. sverify-0.2.0/tests/test_rewrites.py +170 -0
  252. sverify-0.2.0/tests/test_semantic.py +478 -0
  253. sverify-0.2.0/tests/test_skills.py +34 -0
  254. sverify-0.2.0/tests/test_sql.py +731 -0
  255. sverify-0.2.0/tests/test_sql_models.py +176 -0
  256. sverify-0.2.0/tests/test_sql_snowflake.py +463 -0
  257. sverify-0.2.0/tests/test_sql_spark.py +272 -0
  258. sverify-0.2.0/tests/test_text.py +152 -0
  259. sverify-0.2.0/tests/test_time.py +654 -0
  260. sverify-0.2.0/tests/test_windows.py +323 -0
  261. sverify-0.2.0/tests/test_without_datafusion.py +152 -0
  262. sverify-0.2.0/uv.lock +4043 -0
@@ -0,0 +1,14 @@
1
+ /.lake
2
+ /float-bounds/.lake
3
+ /python/.venv
4
+ .hypothesis/
5
+ .pytest_cache/
6
+ __pycache__/
7
+ /python/src/sverify/_bin/
8
+ /python/build/
9
+ /python/dist/
10
+ /python/wheelhouse/
11
+ __marimo__/
12
+ # Great Docs build output (`poe docs`)
13
+ /python/great-docs/
14
+ /python/.great-docs*/
@@ -0,0 +1 @@
1
+ 3.12
sverify-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.5
2
+ Name: sverify
3
+ Version: 0.2.0
4
+ Summary: Contracts and incremental views, proven in Lean, on DuckDB, Polars, DataFusion, Snowflake and Spark
5
+ Project-URL: Homepage, https://github.com/AlexanderNenninger/substrait-lean
6
+ Project-URL: Documentation, https://alexandernenninger.github.io/substrait-lean/
7
+ Project-URL: Source, https://github.com/AlexanderNenninger/substrait-lean
8
+ Project-URL: Issues, https://github.com/AlexanderNenninger/substrait-lean/issues
9
+ Author-email: Alexander Nenninger <alexander.nenninger@live.de>
10
+ Requires-Python: >=3.12
11
+ Requires-Dist: duckdb>=2.0.0
12
+ Requires-Dist: ibis-framework[duckdb]>=12.0.0
13
+ Requires-Dist: pyarrow>=26.0.0
14
+ Requires-Dist: substrait>=0.37.0
15
+ Provides-Extra: all
16
+ Requires-Dist: datafusion==51.0.0; extra == 'all'
17
+ Requires-Dist: deltalake>=1.6.6; extra == 'all'
18
+ Requires-Dist: numpy>=2.5.4; extra == 'all'
19
+ Requires-Dist: polars>=2.1.0; extra == 'all'
20
+ Requires-Dist: pyiceberg[pyarrow]>=0.12.0; extra == 'all'
21
+ Requires-Dist: pyspark>=4.0; extra == 'all'
22
+ Provides-Extra: datafusion
23
+ Requires-Dist: datafusion==51.0.0; extra == 'datafusion'
24
+ Provides-Extra: delta
25
+ Requires-Dist: deltalake>=1.6.6; extra == 'delta'
26
+ Provides-Extra: iceberg
27
+ Requires-Dist: pyiceberg[pyarrow]>=0.12.0; extra == 'iceberg'
28
+ Provides-Extra: polars
29
+ Requires-Dist: numpy>=2.5.4; extra == 'polars'
30
+ Requires-Dist: polars>=2.1.0; extra == 'polars'
31
+ Provides-Extra: spark
32
+ Requires-Dist: pyspark>=4.0; extra == 'spark'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # sverify
36
+
37
+ **Prove a data pipeline's contracts before it runs, get the same result on
38
+ [DuckDB](https://duckdb.org), [Polars](https://pola.rs),
39
+ [DataFusion](https://datafusion.apache.org), [Spark](https://spark.apache.org)
40
+ and Snowflake, and keep it up to date from Delta Lake and Iceberg change
41
+ feeds.**
42
+
43
+ You write a transformation in [Ibis](https://ibis-project.org) or SQL and say
44
+ what it assumes about its inputs (`requires`) and what it guarantees about
45
+ its output (`ensures`). A checker written in [Lean](https://lean-lang.org)
46
+ proves the guarantees for every possible input, or says why it can't. Then:
47
+
48
+ * **Contracts are proved, not tested.** `not_null`, `unique`, ranges and
49
+ relations between columns hold on every input that meets the
50
+ preconditions, and division by zero, overflow and bad casts are ruled out
51
+ before the job runs. Preconditions are checked on the data, or proved from
52
+ Delta and Iceberg metadata.
53
+ * **One plan gives the same answer on every engine.** The checked plan runs
54
+ on DuckDB, Polars or DataFusion, or is written as Snowflake or Spark SQL,
55
+ with NULLs, integer division, NaN, ties and overflow spelled out, or it is
56
+ refused where an engine can't match.
57
+ * **Results are kept up to date from the changes,** by delta plans proved
58
+ equal to a full recompute, read from Delta's Change Data Feed or Iceberg's
59
+ appends, and written to sinks exactly once, with the contract attached.
60
+
61
+ ```bash
62
+ pip install sverify # or: uv add sverify
63
+ pip install 'sverify[all]' # with every engine and table format
64
+ ```
65
+
66
+ The core runs on DuckDB and writes DuckDB, Snowflake and Spark SQL. Extras:
67
+ `datafusion` (also compiles SQL strings), `polars`, `spark` (needs Java 17 or
68
+ 21), `delta`, `iceberg`, or `all`. Wheels bundle the checker as a small
69
+ native binary, for Linux (x86_64, aarch64) and macOS (arm64, x86_64), Python
70
+ 3.12 and later; elsewhere pip builds from the sdist, which needs
71
+ [elan](https://github.com/leanprover/elan).
72
+
73
+ The [API reference](https://alexandernenninger.github.io/substrait-lean/)
74
+ documents every public function and class, for the released version. For
75
+ coding agents, the site also publishes
76
+ [skills](https://alexandernenninger.github.io/substrait-lean/skills.html) for
77
+ writing contracts, pipelines and engine code with sverify
78
+ (`npx skills add https://alexandernenninger.github.io/substrait-lean/`); they
79
+ ship in the wheel too, under `sverify/skills/`.
80
+
81
+ ## In thirty lines
82
+
83
+ ```python
84
+ import pyarrow as pa
85
+ import sverify as sv
86
+ from ibis import _
87
+ from sverify.io import StaticSource
88
+
89
+ orders = StaticSource( # in memory; DeltaSource and IcebergSource work the same way
90
+ pa.table({"id": [1, 2], "customer": ["ada", "bob"], "amount": [30, 5]}), name="orders"
91
+ )
92
+
93
+
94
+ @sv.transform(
95
+ requires={"orders": [_.amount >= 0, sv.unique("id")]},
96
+ ensures=[_.doubled >= 22, _.doubled.notnull(), sv.unique("id")],
97
+ )
98
+ def big_orders(orders):
99
+ return orders.filter(orders.amount > 10).select(orders.id, doubled=orders.amount * 2)
100
+
101
+
102
+ big = big_orders(orders) # proved here, before any data runs
103
+ big.refresh() # computed, on DuckDB by default
104
+ orders.apply(sv.Changes(inserts=[(3, "cy", 50)], deletes=[(1, "ada", 30)]))
105
+ big.refresh(engine="polars") # updated from the changes alone, on another engine
106
+ assert big.to_arrow().to_pylist() == [{"id": 3, "doubled": 100}]
107
+ print(big_orders.last_report)
108
+ # requires orders: (_.amount >= 0)
109
+ # requires orders: unique(id)
110
+ # ensures (_.doubled >= 22): proved
111
+ ```
112
+
113
+ A postcondition that doesn't follow is refused before anything runs:
114
+
115
+ ```python
116
+ @sv.transform(requires={"orders": [_.amount >= 0]}, ensures=[_.total > 5])
117
+ def plus_one(orders):
118
+ return orders.select(orders.id, total=orders.amount + 1)
119
+
120
+
121
+ from sverify.errors import VerificationError
122
+
123
+ try:
124
+ plus_one(orders)
125
+ except VerificationError as e:
126
+ print(e)
127
+ # could not prove: (_.total > 5) (it is TRUE or FALSE)
128
+ ```
129
+
130
+ ## SQL models
131
+
132
+ A transform's body can be SQL, over its inputs by name. It is proved,
133
+ composed and refreshed like any other:
134
+
135
+ ```python
136
+ @sv.transform(
137
+ requires={"orders": ["amount >= 0", "amount IS NOT NULL"]},
138
+ ensures=["total >= 0", sv.unique("customer")],
139
+ )
140
+ def per_customer(orders):
141
+ return sv.sql(
142
+ "SELECT customer, coalesce(sum(amount), 0) AS total FROM orders GROUP BY customer",
143
+ orders=orders,
144
+ )
145
+
146
+
147
+ totals = per_customer(orders) # proved here, like an Ibis transform
148
+ totals.refresh()
149
+ assert sorted(totals.to_arrow().to_pylist(), key=str) == [
150
+ {"customer": "bob", "total": 5},
151
+ {"customer": "cy", "total": 50},
152
+ ]
153
+ ```
154
+
155
+ Several inputs are several keywords (`sv.sql(query, orders=orders,
156
+ customers=customers)`). The SQL is planned by DataFusion
157
+ (`pip install 'sverify[datafusion]'`), or by Isthmus without it.
158
+
159
+ ## Workflows
160
+
161
+ Three complete jobs from a catalogue of 139 real analytical queries, each
162
+ from source tables to a sink, with what goes wrong without the checker. Open
163
+ them in the browser:
164
+
165
+ * [Duplicate card payments](https://molab.marimo.io/github/AlexanderNenninger/substrait-lean/blob/published/notebooks/01_duplicate_payments.py):
166
+ a self-join over a Delta table, refreshed from its Change Data Feed, where
167
+ a watermark job would miss a late duplicate and keep a reversed one.
168
+ * [Sensor alerts](https://molab.marimo.io/github/AlexanderNenninger/substrait-lean/blob/published/notebooks/02_sensor_alerts.py):
169
+ `LAG` over an Iceberg table, where ties and a NaN reading change the alerts,
170
+ and Snowflake and Spark need proved tie-freedom.
171
+ * [An orders mart](https://molab.marimo.io/github/AlexanderNenninger/substrait-lean/blob/published/notebooks/03_orders_mart.py):
172
+ dbt tests as a proved contract on a point-in-time join to a type-2
173
+ dimension, in Ibis and in SQL.
174
+
175
+ The [whole tour](https://github.com/AlexanderNenninger/substrait-lean/tree/main/notebooks)
176
+ explains every feature in a chapter of its own; it runs locally with
177
+ `uvx marimo edit --sandbox notebooks` from a clone.
178
+
179
+ ## The API in brief
180
+
181
+ ```python
182
+ import json
183
+
184
+ import ibis
185
+ from sverify.engines import SNOWFLAKE, to_sql # also DUCKDB, SPARK
186
+ from sverify.io import DeltaSink, DeltaSource # IcebergSource, IcebergSink likewise
187
+
188
+ sv.verify(
189
+ "select id, amount from orders where amount > 0",
190
+ tables={"orders": ibis.table({"id": "int64", "amount": "int64"}, name="orders")},
191
+ ensures=["amount > 0"],
192
+ ) # SQL, checked; returns the report
193
+ to_sql(json.loads(big.plan), SNOWFLAKE) # the checked plan, or sverify.engines.Unsupported
194
+
195
+ # p = paid(DeltaSource("s3://.../orders")).write(DeltaSink("s3://.../paid")) # stored: a source
196
+ # r = revenue(p).write(DeltaSink("s3://.../revenue"))
197
+ # r.refresh(engine="polars"); sv.group(r, other).refresh(); r.to_pandas()
198
+ ```
199
+
200
+ * `@sv.transform(requires=..., ensures=..., on_unproved="error" | "runtime" | "warn")`
201
+ proves a contract on each call; `.last_report` says what was proved and
202
+ what is known about every output column. `sv.unique(...)` states keys.
203
+ * Called on sources (`sverify.io`) or other nodes, transforms build nodes
204
+ that compose directly: a downstream precondition is proved from the
205
+ upstream contract (`CompositionError` names a broken link).
206
+ * Without `.write` in between, transforms fuse into one plan; `.write(sink)`
207
+ stores a node, exactly once per state, and nodes downstream read it as a
208
+ versioned source. `refresh(engine=...)` updates the whole lineage, and
209
+ `sv.group(a, b)` several outputs against the same source versions.
210
+ * Filters, projections, inner and cross joins and `UNION ALL` are
211
+ maintained from the changes; anything else is recomputed and diffed.
212
+ * `sverify.engines` has the engines, SQL dialects and plans
213
+ (`run_sharded` runs a plan per shard and combines the results, with a
214
+ proof that the result is the same); `sverify.errors` the exceptions.
215
+
216
+ **Not supported yet:** DECIMAL columns (keep money as integer cents),
217
+ framed window aggregates, `RANK` and `NTILE`. The repository's README has
218
+ the coverage table, the full guide, and what is proved and what is trusted.
219
+
220
+ ## Configuration
221
+
222
+ * `SVERIFY_CHECK`: path to a `substrait-check` binary to use instead of the
223
+ bundled one, for example your own `lake build`.
224
+ * `SVERIFY_ISTHMUS`: path to an [Isthmus](https://github.com/substrait-io/substrait-java/releases)
225
+ binary (otherwise `isthmus` on `PATH`), to compile SQL strings without
226
+ DataFusion; `sv.connect(sql_producer="isthmus")` picks it, and it is the
227
+ default when DataFusion is not installed. sverify doesn't ship it (~120 MB);
228
+ `scripts/fetch_isthmus.sh <dir>` downloads the pinned release.
229
+ * DataFusion is pinned to 51, because Ibis 12's DataFusion backend doesn't
230
+ connect to later versions.
231
+
232
+ ## Development
233
+
234
+ ```bash
235
+ git clone https://github.com/AlexanderNenninger/substrait-lean
236
+ cd substrait-lean/python
237
+ uv sync # builds substrait-check with lake (needs elan) and links it in
238
+ uv run poe qa # proofs, lint, typecheck, golden, Python tests, notebooks
239
+ ```
240
+
241
+ See the [repository](https://github.com/AlexanderNenninger/substrait-lean) for setup details.
@@ -0,0 +1,207 @@
1
+ # sverify
2
+
3
+ **Prove a data pipeline's contracts before it runs, get the same result on
4
+ [DuckDB](https://duckdb.org), [Polars](https://pola.rs),
5
+ [DataFusion](https://datafusion.apache.org), [Spark](https://spark.apache.org)
6
+ and Snowflake, and keep it up to date from Delta Lake and Iceberg change
7
+ feeds.**
8
+
9
+ You write a transformation in [Ibis](https://ibis-project.org) or SQL and say
10
+ what it assumes about its inputs (`requires`) and what it guarantees about
11
+ its output (`ensures`). A checker written in [Lean](https://lean-lang.org)
12
+ proves the guarantees for every possible input, or says why it can't. Then:
13
+
14
+ * **Contracts are proved, not tested.** `not_null`, `unique`, ranges and
15
+ relations between columns hold on every input that meets the
16
+ preconditions, and division by zero, overflow and bad casts are ruled out
17
+ before the job runs. Preconditions are checked on the data, or proved from
18
+ Delta and Iceberg metadata.
19
+ * **One plan gives the same answer on every engine.** The checked plan runs
20
+ on DuckDB, Polars or DataFusion, or is written as Snowflake or Spark SQL,
21
+ with NULLs, integer division, NaN, ties and overflow spelled out, or it is
22
+ refused where an engine can't match.
23
+ * **Results are kept up to date from the changes,** by delta plans proved
24
+ equal to a full recompute, read from Delta's Change Data Feed or Iceberg's
25
+ appends, and written to sinks exactly once, with the contract attached.
26
+
27
+ ```bash
28
+ pip install sverify # or: uv add sverify
29
+ pip install 'sverify[all]' # with every engine and table format
30
+ ```
31
+
32
+ The core runs on DuckDB and writes DuckDB, Snowflake and Spark SQL. Extras:
33
+ `datafusion` (also compiles SQL strings), `polars`, `spark` (needs Java 17 or
34
+ 21), `delta`, `iceberg`, or `all`. Wheels bundle the checker as a small
35
+ native binary, for Linux (x86_64, aarch64) and macOS (arm64, x86_64), Python
36
+ 3.12 and later; elsewhere pip builds from the sdist, which needs
37
+ [elan](https://github.com/leanprover/elan).
38
+
39
+ The [API reference](https://alexandernenninger.github.io/substrait-lean/)
40
+ documents every public function and class, for the released version. For
41
+ coding agents, the site also publishes
42
+ [skills](https://alexandernenninger.github.io/substrait-lean/skills.html) for
43
+ writing contracts, pipelines and engine code with sverify
44
+ (`npx skills add https://alexandernenninger.github.io/substrait-lean/`); they
45
+ ship in the wheel too, under `sverify/skills/`.
46
+
47
+ ## In thirty lines
48
+
49
+ ```python
50
+ import pyarrow as pa
51
+ import sverify as sv
52
+ from ibis import _
53
+ from sverify.io import StaticSource
54
+
55
+ orders = StaticSource( # in memory; DeltaSource and IcebergSource work the same way
56
+ pa.table({"id": [1, 2], "customer": ["ada", "bob"], "amount": [30, 5]}), name="orders"
57
+ )
58
+
59
+
60
+ @sv.transform(
61
+ requires={"orders": [_.amount >= 0, sv.unique("id")]},
62
+ ensures=[_.doubled >= 22, _.doubled.notnull(), sv.unique("id")],
63
+ )
64
+ def big_orders(orders):
65
+ return orders.filter(orders.amount > 10).select(orders.id, doubled=orders.amount * 2)
66
+
67
+
68
+ big = big_orders(orders) # proved here, before any data runs
69
+ big.refresh() # computed, on DuckDB by default
70
+ orders.apply(sv.Changes(inserts=[(3, "cy", 50)], deletes=[(1, "ada", 30)]))
71
+ big.refresh(engine="polars") # updated from the changes alone, on another engine
72
+ assert big.to_arrow().to_pylist() == [{"id": 3, "doubled": 100}]
73
+ print(big_orders.last_report)
74
+ # requires orders: (_.amount >= 0)
75
+ # requires orders: unique(id)
76
+ # ensures (_.doubled >= 22): proved
77
+ ```
78
+
79
+ A postcondition that doesn't follow is refused before anything runs:
80
+
81
+ ```python
82
+ @sv.transform(requires={"orders": [_.amount >= 0]}, ensures=[_.total > 5])
83
+ def plus_one(orders):
84
+ return orders.select(orders.id, total=orders.amount + 1)
85
+
86
+
87
+ from sverify.errors import VerificationError
88
+
89
+ try:
90
+ plus_one(orders)
91
+ except VerificationError as e:
92
+ print(e)
93
+ # could not prove: (_.total > 5) (it is TRUE or FALSE)
94
+ ```
95
+
96
+ ## SQL models
97
+
98
+ A transform's body can be SQL, over its inputs by name. It is proved,
99
+ composed and refreshed like any other:
100
+
101
+ ```python
102
+ @sv.transform(
103
+ requires={"orders": ["amount >= 0", "amount IS NOT NULL"]},
104
+ ensures=["total >= 0", sv.unique("customer")],
105
+ )
106
+ def per_customer(orders):
107
+ return sv.sql(
108
+ "SELECT customer, coalesce(sum(amount), 0) AS total FROM orders GROUP BY customer",
109
+ orders=orders,
110
+ )
111
+
112
+
113
+ totals = per_customer(orders) # proved here, like an Ibis transform
114
+ totals.refresh()
115
+ assert sorted(totals.to_arrow().to_pylist(), key=str) == [
116
+ {"customer": "bob", "total": 5},
117
+ {"customer": "cy", "total": 50},
118
+ ]
119
+ ```
120
+
121
+ Several inputs are several keywords (`sv.sql(query, orders=orders,
122
+ customers=customers)`). The SQL is planned by DataFusion
123
+ (`pip install 'sverify[datafusion]'`), or by Isthmus without it.
124
+
125
+ ## Workflows
126
+
127
+ Three complete jobs from a catalogue of 139 real analytical queries, each
128
+ from source tables to a sink, with what goes wrong without the checker. Open
129
+ them in the browser:
130
+
131
+ * [Duplicate card payments](https://molab.marimo.io/github/AlexanderNenninger/substrait-lean/blob/published/notebooks/01_duplicate_payments.py):
132
+ a self-join over a Delta table, refreshed from its Change Data Feed, where
133
+ a watermark job would miss a late duplicate and keep a reversed one.
134
+ * [Sensor alerts](https://molab.marimo.io/github/AlexanderNenninger/substrait-lean/blob/published/notebooks/02_sensor_alerts.py):
135
+ `LAG` over an Iceberg table, where ties and a NaN reading change the alerts,
136
+ and Snowflake and Spark need proved tie-freedom.
137
+ * [An orders mart](https://molab.marimo.io/github/AlexanderNenninger/substrait-lean/blob/published/notebooks/03_orders_mart.py):
138
+ dbt tests as a proved contract on a point-in-time join to a type-2
139
+ dimension, in Ibis and in SQL.
140
+
141
+ The [whole tour](https://github.com/AlexanderNenninger/substrait-lean/tree/main/notebooks)
142
+ explains every feature in a chapter of its own; it runs locally with
143
+ `uvx marimo edit --sandbox notebooks` from a clone.
144
+
145
+ ## The API in brief
146
+
147
+ ```python
148
+ import json
149
+
150
+ import ibis
151
+ from sverify.engines import SNOWFLAKE, to_sql # also DUCKDB, SPARK
152
+ from sverify.io import DeltaSink, DeltaSource # IcebergSource, IcebergSink likewise
153
+
154
+ sv.verify(
155
+ "select id, amount from orders where amount > 0",
156
+ tables={"orders": ibis.table({"id": "int64", "amount": "int64"}, name="orders")},
157
+ ensures=["amount > 0"],
158
+ ) # SQL, checked; returns the report
159
+ to_sql(json.loads(big.plan), SNOWFLAKE) # the checked plan, or sverify.engines.Unsupported
160
+
161
+ # p = paid(DeltaSource("s3://.../orders")).write(DeltaSink("s3://.../paid")) # stored: a source
162
+ # r = revenue(p).write(DeltaSink("s3://.../revenue"))
163
+ # r.refresh(engine="polars"); sv.group(r, other).refresh(); r.to_pandas()
164
+ ```
165
+
166
+ * `@sv.transform(requires=..., ensures=..., on_unproved="error" | "runtime" | "warn")`
167
+ proves a contract on each call; `.last_report` says what was proved and
168
+ what is known about every output column. `sv.unique(...)` states keys.
169
+ * Called on sources (`sverify.io`) or other nodes, transforms build nodes
170
+ that compose directly: a downstream precondition is proved from the
171
+ upstream contract (`CompositionError` names a broken link).
172
+ * Without `.write` in between, transforms fuse into one plan; `.write(sink)`
173
+ stores a node, exactly once per state, and nodes downstream read it as a
174
+ versioned source. `refresh(engine=...)` updates the whole lineage, and
175
+ `sv.group(a, b)` several outputs against the same source versions.
176
+ * Filters, projections, inner and cross joins and `UNION ALL` are
177
+ maintained from the changes; anything else is recomputed and diffed.
178
+ * `sverify.engines` has the engines, SQL dialects and plans
179
+ (`run_sharded` runs a plan per shard and combines the results, with a
180
+ proof that the result is the same); `sverify.errors` the exceptions.
181
+
182
+ **Not supported yet:** DECIMAL columns (keep money as integer cents),
183
+ framed window aggregates, `RANK` and `NTILE`. The repository's README has
184
+ the coverage table, the full guide, and what is proved and what is trusted.
185
+
186
+ ## Configuration
187
+
188
+ * `SVERIFY_CHECK`: path to a `substrait-check` binary to use instead of the
189
+ bundled one, for example your own `lake build`.
190
+ * `SVERIFY_ISTHMUS`: path to an [Isthmus](https://github.com/substrait-io/substrait-java/releases)
191
+ binary (otherwise `isthmus` on `PATH`), to compile SQL strings without
192
+ DataFusion; `sv.connect(sql_producer="isthmus")` picks it, and it is the
193
+ default when DataFusion is not installed. sverify doesn't ship it (~120 MB);
194
+ `scripts/fetch_isthmus.sh <dir>` downloads the pinned release.
195
+ * DataFusion is pinned to 51, because Ibis 12's DataFusion backend doesn't
196
+ connect to later versions.
197
+
198
+ ## Development
199
+
200
+ ```bash
201
+ git clone https://github.com/AlexanderNenninger/substrait-lean
202
+ cd substrait-lean/python
203
+ uv sync # builds substrait-check with lake (needs elan) and links it in
204
+ uv run poe qa # proofs, lint, typecheck, golden, Python tests, notebooks
205
+ ```
206
+
207
+ See the [repository](https://github.com/AlexanderNenninger/substrait-lean) for setup details.
@@ -0,0 +1,134 @@
1
+ # Great Docs configuration for the sverify API reference (`uv run poe docs`).
2
+ # See https://posit-dev.github.io/great-docs/user-guide/03-configuration.html
3
+ #
4
+ # The landing page is this directory's README.md (the PyPI page). The notebook
5
+ # tour is linked on molab, not embedded: the notebooks call the native Lean
6
+ # checker, so they can't run in the browser.
7
+
8
+ module: sverify
9
+
10
+ display_name: "sverify"
11
+
12
+ navbar_style: "sky"
13
+
14
+ parser: google
15
+
16
+ # Static analysis (griffe reads the source). Runtime introspection resolves
17
+ # `io.DeltaSource` as an attribute of `sverify`, and `import sverify` doesn't
18
+ # import all of its submodules (`sverify.io` loads delta-rs and pyiceberg
19
+ # lazily).
20
+ dynamic: false
21
+
22
+ # Sections in the order a data engineer looks for things: what a transform
23
+ # promises, how its result is built, stored and kept up to date, where its
24
+ # tables live, where it runs, what can go wrong. The public API is the ten
25
+ # top-level names plus `sverify.io`, `sverify.engines` and `sverify.errors`;
26
+ # everything else is internal (CHANGELOG.md, 0.2.0). `Node`'s results
27
+ # (`Refresh`, `Status`, `Group`) live in `sverify.nodes` and are documented
28
+ # because users hold them, not because they are imported.
29
+ reference:
30
+ - title: Contracts
31
+ desc: >
32
+ State what a transformation assumes and promises, and have it proved
33
+ before any data runs.
34
+ contents:
35
+ - transform
36
+ - verify
37
+ - unique
38
+ - references
39
+ - Report
40
+
41
+ - title: Transforms, nodes and refresh
42
+ desc: >
43
+ A contracted transform called on a source builds a node. Nodes compose,
44
+ are stored with `.write(sink)`, and are kept up to date by `.refresh()`,
45
+ with update plans proved equal to a full recompute.
46
+ contents:
47
+ - Node
48
+ - group
49
+ - Changes
50
+ - nodes.Group
51
+ - nodes.Refresh
52
+ - nodes.Status
53
+ - nodes.NodeStatus
54
+ - nodes.StageRefresh
55
+
56
+ - title: Sources and sinks
57
+ desc: >
58
+ Delta Lake and Iceberg tables as versioned sources, sources of
59
+ preconditions, and sinks that carry the contract (`sverify.io`).
60
+ contents:
61
+ - io.DeltaSource
62
+ - io.DeltaSink
63
+ - io.IcebergSource
64
+ - io.IcebergSink
65
+ - io.StaticSource
66
+ - io.SinkWrite
67
+ - io.Contract
68
+
69
+ - title: Engines and SQL
70
+ desc: >
71
+ Run a checked plan on DuckDB, Polars, DataFusion or Spark, or write it
72
+ as DuckDB, Snowflake or Spark SQL (`sverify.engines`).
73
+ contents:
74
+ - connect
75
+ - engines.run
76
+ - engines.ENGINES
77
+ - engines.Connection
78
+ - engines.create_table
79
+ - engines.table_rows
80
+ - engines.to_sql
81
+ - engines.Dialect
82
+ - engines.DUCKDB
83
+ - engines.SNOWFLAKE
84
+ - engines.SPARK
85
+ - engines.Unsupported
86
+ - engines.run_spark
87
+ - engines.SPARK_CONF
88
+ - engines.run_sharded
89
+
90
+ - title: ASOF joins
91
+ desc: Align two time series, each row with the latest earlier row of the other.
92
+ contents:
93
+ - asof_join
94
+
95
+ - title: Plans
96
+ desc: >
97
+ The low-level layer: Substrait plans, Lean's written form of them, and
98
+ the update plans of incremental maintenance (`sverify.engines`).
99
+ contents:
100
+ - engines.substrait_json
101
+ - engines.isthmus_json
102
+ - engines.SQL_PRODUCERS
103
+ - engines.written_plan
104
+ - engines.run_substrait_json
105
+ - engines.delta_plans
106
+
107
+ - title: Errors
108
+ desc: What sverify raises, and when (`sverify.errors`).
109
+ contents:
110
+ - errors.VerificationError
111
+ - errors.PreconditionError
112
+ - errors.PostconditionError
113
+ - errors.CompositionError
114
+ - errors.NotIncremental
115
+ - errors.AuditError
116
+ - errors.SchemaError
117
+ - errors.SqlError
118
+
119
+ # Agent skills for sverify users, written by hand and shipped in the wheel
120
+ # (`sverify/skills/<name>/SKILL.md`, with their `references/`). The site
121
+ # publishes them under `.well-known/agent-skills/` with a Skills page;
122
+ # `tests/test_skills.py` runs their code.
123
+ skill:
124
+ enabled: true
125
+ well_known: true
126
+ skills:
127
+ - name: sverify
128
+ file: src/sverify/skills/sverify/SKILL.md
129
+ - name: sverify-pipelines
130
+ file: src/sverify/skills/sverify-pipelines/SKILL.md
131
+ - name: sverify-engines
132
+ file: src/sverify/skills/sverify-engines/SKILL.md
133
+
134
+ jupyter: python3
@@ -0,0 +1,60 @@
1
+ """Build `substrait-check` with lake and ship it inside the wheel.
2
+
3
+ The wheel is tagged `py3-none-<platform>`: the Python code is pure, the binary
4
+ is not. A regular wheel gets a stripped copy in `sverify/_bin/`; an editable
5
+ install gets a symlink to `.lake/build/bin/substrait-check`, so a later
6
+ `lake build` takes effect without reinstalling.
7
+
8
+ The Lean project is the repository root, or `lean/` in an unpacked sdist.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import platform
14
+ import shutil
15
+ import subprocess
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
20
+
21
+ BINARY = "substrait-check"
22
+
23
+
24
+ def platform_tag() -> str:
25
+ machine = platform.machine().lower()
26
+ if sys.platform == "darwin":
27
+ # Lean's linker records no real minimum macOS version (`minos 99.0`),
28
+ # so use the oldest one each architecture's toolchain supports.
29
+ return "macosx_11_0_arm64" if machine == "arm64" else "macosx_10_15_x86_64"
30
+ if sys.platform == "linux":
31
+ # `auditwheel repair` turns this into a manylinux tag in CI.
32
+ return f"linux_{machine}"
33
+ raise RuntimeError(f"sverify wheels are not supported on {sys.platform}")
34
+
35
+
36
+ class LakeBuildHook(BuildHookInterface):
37
+ def initialize(self, version: str, build_data: dict) -> None:
38
+ repo = Path(self.root) / "lean"
39
+ if not repo.is_dir():
40
+ repo = Path(self.root).parent
41
+ lake = shutil.which("lake") or str(Path.home() / ".elan" / "bin" / "lake")
42
+ if not Path(lake).exists():
43
+ raise RuntimeError("building sverify needs `lake` (install elan) on PATH")
44
+ subprocess.run([lake, "build", BINARY], cwd=repo, check=True)
45
+
46
+ built = repo / ".lake" / "build" / "bin" / BINARY
47
+ if version == "editable":
48
+ link = Path(self.root) / "src" / "sverify" / "_bin" / BINARY
49
+ link.parent.mkdir(exist_ok=True)
50
+ link.unlink(missing_ok=True)
51
+ link.symlink_to(built)
52
+ else:
53
+ staged = Path(self.root) / "build" / BINARY
54
+ staged.parent.mkdir(exist_ok=True)
55
+ shutil.copy2(built, staged)
56
+ subprocess.run(["strip", str(staged)], check=True)
57
+ build_data["force_include"][str(staged)] = f"sverify/_bin/{BINARY}"
58
+
59
+ build_data["pure_python"] = False
60
+ build_data["tag"] = f"py3-none-{platform_tag()}"