datahike-browser-tests 1.0.0

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 (324) hide show
  1. package/.circleci/config.yml +405 -0
  2. package/.circleci/scripts/gen_ci.clj +194 -0
  3. package/.cirrus.yml +60 -0
  4. package/.clj-kondo/babashka/sci/config.edn +1 -0
  5. package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
  6. package/.clj-kondo/config.edn +95 -0
  7. package/.dir-locals.el +2 -0
  8. package/.github/FUNDING.yml +3 -0
  9. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
  10. package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
  11. package/.github/ISSUE_TEMPLATE/config.yml +6 -0
  12. package/.github/pull_request_template.md +24 -0
  13. package/.github/workflows/native-image.yml +84 -0
  14. package/LICENSE +203 -0
  15. package/README.md +273 -0
  16. package/bb/deps.edn +9 -0
  17. package/bb/resources/github-fingerprints +3 -0
  18. package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
  19. package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
  20. package/bb/resources/native-image-tests/run-native-image-tests +74 -0
  21. package/bb/resources/native-image-tests/run-python-tests +22 -0
  22. package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
  23. package/bb/resources/native-image-tests/testconfig.edn +5 -0
  24. package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  25. package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
  26. package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
  27. package/bb/resources/template/pom.xml +22 -0
  28. package/bb/src/tools/build.clj +132 -0
  29. package/bb/src/tools/clj_kondo.clj +32 -0
  30. package/bb/src/tools/deploy.clj +26 -0
  31. package/bb/src/tools/examples.clj +19 -0
  32. package/bb/src/tools/npm.clj +100 -0
  33. package/bb/src/tools/python.clj +14 -0
  34. package/bb/src/tools/release.clj +94 -0
  35. package/bb/src/tools/test.clj +148 -0
  36. package/bb/src/tools/version.clj +47 -0
  37. package/bb.edn +269 -0
  38. package/benchmark/src/benchmark/cli.clj +195 -0
  39. package/benchmark/src/benchmark/compare.clj +157 -0
  40. package/benchmark/src/benchmark/config.clj +316 -0
  41. package/benchmark/src/benchmark/measure.clj +187 -0
  42. package/benchmark/src/benchmark/store.clj +190 -0
  43. package/benchmark/test/benchmark/measure_test.clj +156 -0
  44. package/build.clj +30 -0
  45. package/config.edn +49 -0
  46. package/deps.edn +138 -0
  47. package/dev/sandbox.clj +82 -0
  48. package/dev/sandbox.cljs +127 -0
  49. package/dev/sandbox_benchmarks.clj +27 -0
  50. package/dev/sandbox_client.clj +87 -0
  51. package/dev/sandbox_transact_bench.clj +109 -0
  52. package/dev/user.clj +79 -0
  53. package/doc/README.md +96 -0
  54. package/doc/adl/README.md +6 -0
  55. package/doc/adl/adr-000-adr.org +28 -0
  56. package/doc/adl/adr-001-attribute-references.org +15 -0
  57. package/doc/adl/adr-002-build-tooling.org +54 -0
  58. package/doc/adl/adr-003-db-meta-data.md +52 -0
  59. package/doc/adl/adr-004-github-flow.md +40 -0
  60. package/doc/adl/adr-XYZ-template.md +30 -0
  61. package/doc/adl/index.org +3 -0
  62. package/doc/assets/datahike-logo.svg +3 -0
  63. package/doc/assets/datahiking-invoice.org +85 -0
  64. package/doc/assets/hhtree2.png +0 -0
  65. package/doc/assets/network_topology.svg +624 -0
  66. package/doc/assets/perf.png +0 -0
  67. package/doc/assets/schema_mindmap.mm +132 -0
  68. package/doc/assets/schema_mindmap.svg +970 -0
  69. package/doc/assets/temporal_index.mm +74 -0
  70. package/doc/backend-development.md +78 -0
  71. package/doc/bb-pod.md +89 -0
  72. package/doc/benchmarking.md +360 -0
  73. package/doc/bindings/edn-conversion.md +383 -0
  74. package/doc/cli.md +162 -0
  75. package/doc/cljdoc.edn +27 -0
  76. package/doc/cljs-support.md +133 -0
  77. package/doc/config.md +406 -0
  78. package/doc/contributing.md +114 -0
  79. package/doc/datalog-vs-sql.md +210 -0
  80. package/doc/datomic_differences.md +109 -0
  81. package/doc/development/pull-api-ns.md +186 -0
  82. package/doc/development/pull-frame-state-diagram.jpg +0 -0
  83. package/doc/distributed.md +566 -0
  84. package/doc/entity_spec.md +92 -0
  85. package/doc/gc.md +273 -0
  86. package/doc/java-api.md +808 -0
  87. package/doc/javascript-api.md +421 -0
  88. package/doc/libdatahike.md +86 -0
  89. package/doc/logging_and_error_handling.md +43 -0
  90. package/doc/norms.md +66 -0
  91. package/doc/schema-migration.md +85 -0
  92. package/doc/schema.md +287 -0
  93. package/doc/storage-backends.md +363 -0
  94. package/doc/store-id-refactoring.md +596 -0
  95. package/doc/time_variance.md +325 -0
  96. package/doc/unstructured.md +167 -0
  97. package/doc/versioning.md +261 -0
  98. package/examples/basic/README.md +19 -0
  99. package/examples/basic/deps.edn +6 -0
  100. package/examples/basic/docker-compose.yml +13 -0
  101. package/examples/basic/src/examples/core.clj +60 -0
  102. package/examples/basic/src/examples/schema.clj +155 -0
  103. package/examples/basic/src/examples/store.clj +60 -0
  104. package/examples/basic/src/examples/time_travel.clj +185 -0
  105. package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
  106. package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
  107. package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
  108. package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
  109. package/examples/java/README.md +162 -0
  110. package/examples/java/pom.xml +62 -0
  111. package/examples/java/src/main/java/examples/QuickStart.java +115 -0
  112. package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
  113. package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
  114. package/flake.lock +27 -0
  115. package/flake.nix +27 -0
  116. package/http-server/datahike/http/middleware.clj +75 -0
  117. package/http-server/datahike/http/server.clj +269 -0
  118. package/java/src/datahike/java/Database.java +274 -0
  119. package/java/src/datahike/java/Datahike.java +281 -0
  120. package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
  121. package/java/src/datahike/java/DatahikeTest.java +370 -0
  122. package/java/src/datahike/java/EDN.java +170 -0
  123. package/java/src/datahike/java/IEntity.java +11 -0
  124. package/java/src/datahike/java/Keywords.java +161 -0
  125. package/java/src/datahike/java/SchemaFlexibility.java +52 -0
  126. package/java/src/datahike/java/Util.java +219 -0
  127. package/karma.conf.js +19 -0
  128. package/libdatahike/compile-cpp +7 -0
  129. package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
  130. package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
  131. package/libdatahike/src/test_cpp.cpp +61 -0
  132. package/npm-package/PUBLISHING.md +140 -0
  133. package/npm-package/README.md +226 -0
  134. package/npm-package/package.template.json +34 -0
  135. package/npm-package/test-isomorphic.ts +281 -0
  136. package/npm-package/test.js +557 -0
  137. package/npm-package/typescript-test.ts +70 -0
  138. package/package.json +16 -0
  139. package/pydatahike/README.md +569 -0
  140. package/pydatahike/pyproject.toml +91 -0
  141. package/pydatahike/setup.py +42 -0
  142. package/pydatahike/src/datahike/__init__.py +134 -0
  143. package/pydatahike/src/datahike/_native.py +250 -0
  144. package/pydatahike/src/datahike/_version.py +2 -0
  145. package/pydatahike/src/datahike/database.py +722 -0
  146. package/pydatahike/src/datahike/edn.py +311 -0
  147. package/pydatahike/src/datahike/py.typed +0 -0
  148. package/pydatahike/tests/conftest.py +17 -0
  149. package/pydatahike/tests/test_basic.py +170 -0
  150. package/pydatahike/tests/test_database.py +51 -0
  151. package/pydatahike/tests/test_edn_conversion.py +299 -0
  152. package/pydatahike/tests/test_query.py +99 -0
  153. package/pydatahike/tests/test_schema.py +55 -0
  154. package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
  155. package/resources/example_server.edn +4 -0
  156. package/shadow-cljs.edn +56 -0
  157. package/src/data_readers.clj +7 -0
  158. package/src/datahike/api/impl.cljc +176 -0
  159. package/src/datahike/api/specification.cljc +633 -0
  160. package/src/datahike/api/types.cljc +261 -0
  161. package/src/datahike/api.cljc +41 -0
  162. package/src/datahike/array.cljc +99 -0
  163. package/src/datahike/cli.clj +166 -0
  164. package/src/datahike/cljs.cljs +6 -0
  165. package/src/datahike/codegen/cli.clj +406 -0
  166. package/src/datahike/codegen/clj_kondo.clj +291 -0
  167. package/src/datahike/codegen/java.clj +403 -0
  168. package/src/datahike/codegen/naming.cljc +33 -0
  169. package/src/datahike/codegen/native.clj +559 -0
  170. package/src/datahike/codegen/pod.clj +488 -0
  171. package/src/datahike/codegen/python.clj +838 -0
  172. package/src/datahike/codegen/report.clj +55 -0
  173. package/src/datahike/codegen/typescript.clj +262 -0
  174. package/src/datahike/codegen/validation.clj +145 -0
  175. package/src/datahike/config.cljc +294 -0
  176. package/src/datahike/connections.cljc +16 -0
  177. package/src/datahike/connector.cljc +265 -0
  178. package/src/datahike/constants.cljc +142 -0
  179. package/src/datahike/core.cljc +297 -0
  180. package/src/datahike/datom.cljc +459 -0
  181. package/src/datahike/db/interface.cljc +119 -0
  182. package/src/datahike/db/search.cljc +305 -0
  183. package/src/datahike/db/transaction.cljc +937 -0
  184. package/src/datahike/db/utils.cljc +338 -0
  185. package/src/datahike/db.cljc +956 -0
  186. package/src/datahike/experimental/unstructured.cljc +126 -0
  187. package/src/datahike/experimental/versioning.cljc +172 -0
  188. package/src/datahike/externs.js +31 -0
  189. package/src/datahike/gc.cljc +69 -0
  190. package/src/datahike/http/client.clj +188 -0
  191. package/src/datahike/http/writer.clj +79 -0
  192. package/src/datahike/impl/entity.cljc +218 -0
  193. package/src/datahike/index/interface.cljc +93 -0
  194. package/src/datahike/index/persistent_set.cljc +469 -0
  195. package/src/datahike/index/utils.cljc +44 -0
  196. package/src/datahike/index.cljc +32 -0
  197. package/src/datahike/js/api.cljs +172 -0
  198. package/src/datahike/js/api_macros.clj +22 -0
  199. package/src/datahike/js.cljs +163 -0
  200. package/src/datahike/json.cljc +209 -0
  201. package/src/datahike/lru.cljc +146 -0
  202. package/src/datahike/migrate.clj +39 -0
  203. package/src/datahike/norm/norm.clj +245 -0
  204. package/src/datahike/online_gc.cljc +252 -0
  205. package/src/datahike/pod.clj +155 -0
  206. package/src/datahike/pull_api.cljc +325 -0
  207. package/src/datahike/query.cljc +1945 -0
  208. package/src/datahike/query_stats.cljc +88 -0
  209. package/src/datahike/readers.cljc +62 -0
  210. package/src/datahike/remote.cljc +218 -0
  211. package/src/datahike/schema.cljc +228 -0
  212. package/src/datahike/schema_cache.cljc +42 -0
  213. package/src/datahike/spec.cljc +101 -0
  214. package/src/datahike/store.cljc +80 -0
  215. package/src/datahike/tools.cljc +308 -0
  216. package/src/datahike/transit.cljc +80 -0
  217. package/src/datahike/writer.cljc +239 -0
  218. package/src/datahike/writing.cljc +362 -0
  219. package/src/deps.cljs +1 -0
  220. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
  221. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
  222. package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
  223. package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
  224. package/test/datahike/integration_test/config_record_file_test.clj +14 -0
  225. package/test/datahike/integration_test/config_record_test.clj +14 -0
  226. package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
  227. package/test/datahike/integration_test/return_map_test.clj +62 -0
  228. package/test/datahike/integration_test.cljc +67 -0
  229. package/test/datahike/norm/norm_test.clj +124 -0
  230. package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
  231. package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
  232. package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
  233. package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
  234. package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
  235. package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
  236. package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
  237. package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
  238. package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
  239. package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
  240. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
  241. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
  242. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
  243. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
  244. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
  245. package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
  246. package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
  247. package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
  248. package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
  249. package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
  250. package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
  251. package/test/datahike/test/api_test.cljc +895 -0
  252. package/test/datahike/test/array_test.cljc +40 -0
  253. package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
  254. package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
  255. package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
  256. package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
  257. package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
  258. package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
  259. package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
  260. package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
  261. package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
  262. package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
  263. package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
  264. package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
  265. package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
  266. package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
  267. package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
  268. package/test/datahike/test/attribute_refs/utils.cljc +128 -0
  269. package/test/datahike/test/cache_test.cljc +38 -0
  270. package/test/datahike/test/components_test.cljc +92 -0
  271. package/test/datahike/test/config_test.cljc +158 -0
  272. package/test/datahike/test/core_test.cljc +105 -0
  273. package/test/datahike/test/datom_test.cljc +44 -0
  274. package/test/datahike/test/db_test.cljc +54 -0
  275. package/test/datahike/test/entity_spec_test.cljc +159 -0
  276. package/test/datahike/test/entity_test.cljc +103 -0
  277. package/test/datahike/test/explode_test.cljc +143 -0
  278. package/test/datahike/test/filter_test.cljc +75 -0
  279. package/test/datahike/test/gc_test.cljc +159 -0
  280. package/test/datahike/test/http/server_test.clj +192 -0
  281. package/test/datahike/test/http/writer_test.clj +86 -0
  282. package/test/datahike/test/ident_test.cljc +32 -0
  283. package/test/datahike/test/index_test.cljc +345 -0
  284. package/test/datahike/test/insert.cljc +125 -0
  285. package/test/datahike/test/java_bindings_test.clj +6 -0
  286. package/test/datahike/test/listen_test.cljc +41 -0
  287. package/test/datahike/test/lookup_refs_test.cljc +266 -0
  288. package/test/datahike/test/lru_test.cljc +27 -0
  289. package/test/datahike/test/migrate_test.clj +297 -0
  290. package/test/datahike/test/model/core.cljc +376 -0
  291. package/test/datahike/test/model/invariant.cljc +142 -0
  292. package/test/datahike/test/model/rng.cljc +82 -0
  293. package/test/datahike/test/model_test.clj +217 -0
  294. package/test/datahike/test/nodejs_test.cljs +262 -0
  295. package/test/datahike/test/online_gc_test.cljc +475 -0
  296. package/test/datahike/test/pod_test.clj +369 -0
  297. package/test/datahike/test/pull_api_test.cljc +474 -0
  298. package/test/datahike/test/purge_test.cljc +144 -0
  299. package/test/datahike/test/query_aggregates_test.cljc +101 -0
  300. package/test/datahike/test/query_find_specs_test.cljc +52 -0
  301. package/test/datahike/test/query_fns_test.cljc +523 -0
  302. package/test/datahike/test/query_interop_test.cljc +47 -0
  303. package/test/datahike/test/query_not_test.cljc +189 -0
  304. package/test/datahike/test/query_or_test.cljc +158 -0
  305. package/test/datahike/test/query_pull_test.cljc +147 -0
  306. package/test/datahike/test/query_rules_test.cljc +248 -0
  307. package/test/datahike/test/query_stats_test.cljc +218 -0
  308. package/test/datahike/test/query_test.cljc +984 -0
  309. package/test/datahike/test/schema_test.cljc +424 -0
  310. package/test/datahike/test/specification_test.cljc +30 -0
  311. package/test/datahike/test/store_test.cljc +78 -0
  312. package/test/datahike/test/stress_test.cljc +57 -0
  313. package/test/datahike/test/time_variance_test.cljc +518 -0
  314. package/test/datahike/test/tools_test.clj +134 -0
  315. package/test/datahike/test/transact_test.cljc +518 -0
  316. package/test/datahike/test/tuples_test.cljc +564 -0
  317. package/test/datahike/test/unstructured_test.cljc +291 -0
  318. package/test/datahike/test/upsert_impl_test.cljc +205 -0
  319. package/test/datahike/test/upsert_test.cljc +363 -0
  320. package/test/datahike/test/utils.cljc +110 -0
  321. package/test/datahike/test/validation_test.cljc +48 -0
  322. package/test/datahike/test/versioning_test.cljc +56 -0
  323. package/test/datahike/test.cljc +66 -0
  324. package/tests.edn +24 -0
@@ -0,0 +1,569 @@
1
+ # Datahike Python Bindings
2
+
3
+ **Status: Beta** - API is functional and tested, but may receive breaking changes as we refine the bindings.
4
+
5
+ Python bindings for [Datahike](https://github.com/replikativ/datahike), a durable Datalog database powered by an efficient Datalog query engine.
6
+
7
+ ## Features
8
+
9
+ - **Datalog queries** - Expressive declarative queries
10
+ - **Durable storage** - Persistent databases with ACID transactions
11
+ - **Time travel** - Query database history and point-in-time snapshots
12
+ - **Flexible schema** - Schema-on-write or schema-on-read
13
+ - **Pull API** - Recursive pattern-based entity retrieval
14
+ - **Multiple backends** - Memory, file system, and more
15
+ - **Pythonic API** - Work with Python dicts, not EDN strings
16
+
17
+ ## Installation
18
+
19
+ ### Prerequisites
20
+
21
+ - Python 3.8+
22
+ - GraalVM (for building native library)
23
+ - Babashka (for build tasks)
24
+
25
+ ### Building from Source
26
+
27
+ ```bash
28
+ # Clone the Datahike repository
29
+ git clone https://github.com/replikativ/datahike.git
30
+ cd datahike
31
+
32
+ # Build the native library
33
+ bb ni-compile
34
+
35
+ # Install Python package
36
+ pip install ./pydatahike
37
+ ```
38
+
39
+ ### Setting Library Path
40
+
41
+ If the library isn't found automatically, set the environment variable:
42
+
43
+ ```bash
44
+ export LIBDATAHIKE_PATH=/path/to/datahike/libdatahike/target/libdatahike.so
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ > **Note:** Memory backend requires UUID identifiers. Use `str(uuid.uuid4())` to generate unique IDs. This is required by the underlying konserve store and is essential for distributed database tracking.
50
+
51
+ ```python
52
+ import uuid
53
+ from datahike import Database
54
+
55
+ # Create database
56
+ db = Database.memory(str(uuid.uuid4()))
57
+ db.create()
58
+
59
+ # Transact data
60
+ db.transact([
61
+ {"name": "Alice", "age": 30},
62
+ {"name": "Bob", "age": 25}
63
+ ])
64
+
65
+ # Query with Datalog
66
+ result = db.q('[:find ?name ?age :where [?e :name ?name] [?e :age ?age]]')
67
+ print(result) # [['Alice', 30], ['Bob', 25]]
68
+
69
+ # Pull entity
70
+ entity = db.pull('[:name :age]', 1)
71
+ print(entity) # {':name': 'Alice', ':age': 30}
72
+
73
+ # Cleanup
74
+ db.delete()
75
+ ```
76
+
77
+ ## High-Level API (Recommended)
78
+
79
+ The high-level API provides a Pythonic interface using native Python data structures.
80
+
81
+ ### Database Class
82
+
83
+ ```python
84
+ import uuid
85
+ from datahike import Database
86
+
87
+ # Create in-memory database
88
+ db = Database.memory(str(uuid.uuid4()))
89
+ db.create()
90
+
91
+ # Create file-based database
92
+ db = Database.file("/tmp/mydb")
93
+ db.create()
94
+
95
+ # Create with full configuration (Python dict)
96
+ db = Database({
97
+ "store": {
98
+ "backend": ":memory",
99
+ "id": "mydb"
100
+ },
101
+ "schema-flexibility": ":read",
102
+ "keep-history?": True
103
+ })
104
+ db.create()
105
+
106
+ # Don't forget to delete when done
107
+ db.delete()
108
+ ```
109
+
110
+ ### Context Manager (Auto-Cleanup)
111
+
112
+ ```python
113
+ import uuid
114
+ from datahike import database
115
+
116
+ # Database automatically created and deleted
117
+ with database(backend=':memory', id=str(uuid.uuid4())) as db:
118
+ db.transact([{"name": "Alice"}])
119
+ result = db.q('[:find ?name :where [?e :name ?name]]')
120
+ print(result) # [['Alice']]
121
+ # Database deleted when context exits
122
+ ```
123
+
124
+ ### Transactions with Python Dicts
125
+
126
+ ```python
127
+ # Single entity
128
+ db.transact({"name": "Alice", "age": 30})
129
+
130
+ # Multiple entities
131
+ db.transact([
132
+ {"name": "Alice", "age": 30},
133
+ {"name": "Bob", "age": 25}
134
+ ])
135
+
136
+ # Schema transaction with EDN helpers
137
+ from datahike import edn, kw
138
+
139
+ db.transact([{
140
+ kw.DB_IDENT: edn.keyword("person/name"),
141
+ kw.DB_VALUE_TYPE: kw.STRING,
142
+ kw.DB_CARDINALITY: kw.ONE,
143
+ kw.DB_DOC: "Person's full name"
144
+ }])
145
+ ```
146
+
147
+ ### Queries
148
+
149
+ ```python
150
+ # Simple query
151
+ result = db.q('[:find ?name :where [?e :name ?name]]')
152
+
153
+ # Query with parameters
154
+ result = db.q(
155
+ '[:find ?e :in $ ?name :where [?e :name ?name]]',
156
+ ('param', '"Alice"')
157
+ )
158
+
159
+ # Query multiple databases
160
+ other_db = Database.memory(str(uuid.uuid4()))
161
+ result = db.q(
162
+ '[:find ?name :in $ $2 :where [$ ?e :name ?name] [$2 ?e :active true]]',
163
+ other_db
164
+ )
165
+ ```
166
+
167
+ ### Time Travel
168
+
169
+ ```python
170
+ import time
171
+
172
+ # Store current timestamp
173
+ t1 = int(time.time() * 1000)
174
+ db.transact({"name": "Alice", "status": ":active"})
175
+
176
+ time.sleep(0.1)
177
+ t2 = int(time.time() * 1000)
178
+ db.transact({"name": "Bob", "status": ":inactive"})
179
+
180
+ # Query current state
181
+ current = db.q('[:find ?name :where [?e :name ?name]]')
182
+ print(current) # [['Alice'], ['Bob']]
183
+
184
+ # Query as of t1 (before Bob was added)
185
+ past = db.as_of(t1)
186
+ result = past.q('[:find ?name :where [?e :name ?name]]')
187
+ print(result) # [['Alice']]
188
+
189
+ # Query changes since t1
190
+ changes = db.since(t1)
191
+ result = changes.q('[:find ?name :where [?e :name ?name]]')
192
+ print(result) # [['Bob']]
193
+
194
+ # Query full history
195
+ history = db.history
196
+ result = history.q('[:find ?name :where [?e :name ?name]]')
197
+ print(result) # All historical values
198
+ ```
199
+
200
+ ## EDN Conversion Rules
201
+
202
+ Datahike uses [EDN (Extensible Data Notation)](https://github.com/edn-format/edn), Clojure's data format. The Python bindings automatically convert between Python and EDN using simple, predictable rules:
203
+
204
+ ### The Universal Rule
205
+
206
+ > **Keys are always keywordized. Values starting with `:` become keywords, everything else remains literal.**
207
+
208
+ ### Examples
209
+
210
+ ```python
211
+ # Python dict → EDN map
212
+ {
213
+ "store": {
214
+ "backend": ":memory", # ":memory" → :memory (keyword)
215
+ "id": "test" # "test" → "test" (string)
216
+ },
217
+ "schema-flexibility": ":read", # ":read" → :read (keyword)
218
+ "keep-history?": True # True → true (boolean)
219
+ }
220
+ # → {:store {:backend :memory :id "test"}
221
+ # :schema-flexibility :read
222
+ # :keep-history? true}
223
+
224
+ # Transaction data
225
+ [
226
+ {"name": "Alice", "status": ":active"}, # "Alice" → string, ":active" → keyword
227
+ {"name": "Bob", "age": 25} # 25 → number
228
+ ]
229
+ # → [{:name "Alice" :status :active}
230
+ # {:name "Bob" :age 25}]
231
+ ```
232
+
233
+ ### Escape Hatches
234
+
235
+ For fine-grained control, use the `edn` helper module:
236
+
237
+ ```python
238
+ from datahike import edn, kw
239
+
240
+ # Explicit keyword construction
241
+ edn.keyword("name") # → :name
242
+ edn.keyword("name", "person") # → :person/name
243
+
244
+ # Force string (even with : prefix)
245
+ edn.string(":literal-colon") # → ":literal-colon" (string, not keyword)
246
+
247
+ # UUID and timestamps
248
+ edn.uuid("550e8400-e29b-41d4-a716-446655440000") # → #uuid "..."
249
+ edn.inst("2024-01-01T00:00:00Z") # → #inst "..."
250
+
251
+ # Pre-defined constants (avoid typos)
252
+ kw.DB_ID # → ":db/id"
253
+ kw.DB_IDENT # → ":db/ident"
254
+ kw.DB_VALUE_TYPE # → ":db/valueType"
255
+ kw.STRING # → ":db.type/string"
256
+ kw.ONE # → ":db.cardinality/one"
257
+ ```
258
+
259
+ For complete EDN conversion rules and edge cases, see [EDN Conversion Documentation](../doc/bindings/edn-conversion.md).
260
+
261
+ ## Usage Examples
262
+
263
+ ### Schema Definition
264
+
265
+ ```python
266
+ import uuid
267
+ from datahike import Database, edn, kw
268
+
269
+ db = Database.memory(str(uuid.uuid4()))
270
+ db.create()
271
+
272
+ # Define schema using Python dicts and EDN helpers
273
+ schema = [{
274
+ kw.DB_IDENT: edn.keyword("person/name"),
275
+ kw.DB_VALUE_TYPE: kw.STRING,
276
+ kw.DB_CARDINALITY: kw.ONE,
277
+ kw.DB_DOC: "Person's full name"
278
+ }, {
279
+ kw.DB_IDENT: edn.keyword("person/age"),
280
+ kw.DB_VALUE_TYPE: kw.LONG,
281
+ kw.DB_CARDINALITY: kw.ONE
282
+ }, {
283
+ kw.DB_IDENT: edn.keyword("person/friends"),
284
+ kw.DB_VALUE_TYPE: kw.REF,
285
+ kw.DB_CARDINALITY: kw.MANY,
286
+ kw.DB_DOC: "Person's friends (refs)"
287
+ }]
288
+
289
+ db.transact(schema)
290
+
291
+ # Get schema
292
+ db_schema = db.schema()
293
+ print(db_schema)
294
+ ```
295
+
296
+ ### Pull API
297
+
298
+ ```python
299
+ # Pull single entity
300
+ entity = db.pull('[:name :age]', 1)
301
+
302
+ # Pull with wildcard
303
+ entity = db.pull('[*]', 1)
304
+
305
+ # Pull with relationships
306
+ pattern = '[:name {:friends [:name]}]'
307
+ entity = db.pull(pattern, 1)
308
+
309
+ # Pull multiple entities
310
+ entities = db.pull_many('[:name]', [1, 2, 3])
311
+ ```
312
+
313
+ ### Custom Backends
314
+
315
+ The dict-based API naturally supports custom backends with arbitrary configuration:
316
+
317
+ ```python
318
+ # Custom S3 backend (hypothetical)
319
+ db = Database({
320
+ "store": {
321
+ "backend": ":my-s3",
322
+ "bucket": "my-bucket",
323
+ "region": "us-west-2",
324
+ "encryption": {
325
+ "type": ":aes256",
326
+ "key-id": "secret-key"
327
+ }
328
+ }
329
+ })
330
+ db.create()
331
+ ```
332
+
333
+ ### Index Operations
334
+
335
+ ```python
336
+ # Get datoms from index
337
+ datoms = db.datoms(':eavt')
338
+
339
+ # Seek to position in index
340
+ datoms = db.seek_datoms(':avet', [':name', '"Alice"'])
341
+
342
+ # Get index range
343
+ range_data = db.index_range(':name', '"A"', '"M"')
344
+ ```
345
+
346
+ ### Error Handling
347
+
348
+ ```python
349
+ import uuid
350
+ from datahike import Database, DatahikeException
351
+
352
+ db = Database.memory(str(uuid.uuid4()))
353
+ db.create()
354
+
355
+ try:
356
+ result = db.q('[:find ?e :where [?e :nonexistent]]')
357
+ except DatahikeException as e:
358
+ print(f"Query failed: {e}")
359
+ ```
360
+
361
+ ## Advanced: Low-Level API
362
+
363
+ For advanced use cases, you can use the low-level API that works directly with EDN strings:
364
+
365
+ ```python
366
+ from datahike import create_database, delete_database, transact, q
367
+
368
+ # Create database with EDN config
369
+ config = '{:store {:backend :memory :id "lowlevel"}}'
370
+ create_database(config)
371
+
372
+ # Transact EDN data
373
+ transact(config, '[{:name "Alice" :age 30}]')
374
+
375
+ # Query with explicit inputs
376
+ result = q(
377
+ '[:find ?name ?age :where [?e :name ?name] [?e :age ?age]]',
378
+ [('db', config)],
379
+ output_format='cbor'
380
+ )
381
+
382
+ # Cleanup
383
+ delete_database(config)
384
+ ```
385
+
386
+ The low-level API gives you full control over EDN serialization and output formats, but requires manual string construction.
387
+
388
+ ## Output Formats
389
+
390
+ All query and retrieval functions support multiple output formats:
391
+
392
+ - `cbor` (default) - Compact binary format, best for structured data
393
+ - `json` - Human-readable, good for debugging
394
+ - `edn` - Clojure data format (returned as string)
395
+
396
+ ```python
397
+ # High-level API
398
+ result = db.q(query, output_format='json')
399
+
400
+ # Low-level API
401
+ result = q(query, inputs, output_format='edn')
402
+ ```
403
+
404
+ ## API Reference
405
+
406
+ ### High-Level API
407
+
408
+ #### Database Class
409
+
410
+ **Factory Methods:**
411
+ - `Database.memory(id)` - Create in-memory database config. **Important:** `id` must be a UUID string (use `str(uuid.uuid4())`)
412
+ - `Database.file(path)` - Create file-based database config
413
+ - `Database(config_dict)` - Create from Python dict
414
+ - `Database(edn_string)` - Create from EDN string
415
+
416
+ **Lifecycle:**
417
+ - `db.create()` - Create the database
418
+ - `db.delete()` - Delete the database
419
+ - `db.exists()` - Check if database exists
420
+
421
+ **Transactions:**
422
+ - `db.transact(data, input_format='json')` - Execute transaction with Python dict/list
423
+
424
+ **Queries:**
425
+ - `db.q(query, *args, **kwargs)` - Execute Datalog query
426
+ - `db.pull(selector, eid)` - Pull entity by pattern
427
+ - `db.pull_many(selector, eids)` - Pull multiple entities
428
+ - `db.entity(eid)` - Get entity by ID
429
+
430
+ **Time Travel:**
431
+ - `db.as_of(timestamp_ms)` - Return DatabaseSnapshot at point in time
432
+ - `db.since(timestamp_ms)` - Return DatabaseSnapshot with changes since time
433
+ - `db.history` - Return DatabaseSnapshot with full history
434
+
435
+ **Schema & Metadata:**
436
+ - `db.schema()` - Get database schema
437
+ - `db.reverse_schema()` - Get reverse schema mapping
438
+ - `db.metrics()` - Get database metrics
439
+
440
+ **Index Operations:**
441
+ - `db.datoms(index)` - Get datoms from index
442
+ - `db.seek_datoms(index, components)` - Seek to position in index
443
+ - `db.index_range(attr, start, end)` - Get index range
444
+
445
+ **Maintenance:**
446
+ - `db.gc_storage()` - Garbage collect storage
447
+
448
+ #### Context Manager
449
+
450
+ - `database(**kwargs)` - Context manager for automatic database lifecycle
451
+ - `database(config_dict)` - Context manager with dict config
452
+
453
+ #### EDN Helpers
454
+
455
+ **Types:**
456
+ - `edn.keyword(name, namespace=None)` - Create EDN keyword
457
+ - `edn.symbol(name, namespace=None)` - Create EDN symbol
458
+ - `edn.uuid(value)` - Create EDN UUID
459
+ - `edn.inst(value)` - Create EDN instant (timestamp)
460
+ - `edn.string(value)` - Force string (escape : prefix)
461
+
462
+ **Constants (`kw` object):**
463
+ - `kw.DB_ID`, `kw.DB_IDENT`
464
+ - `kw.DB_VALUE_TYPE`, `kw.DB_CARDINALITY`, `kw.DB_DOC`
465
+ - `kw.DB_UNIQUE`, `kw.DB_IS_COMPONENT`, `kw.DB_NO_HISTORY`
466
+ - `kw.STRING`, `kw.BOOLEAN`, `kw.LONG`, `kw.BIGINT`, `kw.FLOAT`, `kw.DOUBLE`
467
+ - `kw.INSTANT`, `kw.UUID_TYPE`, `kw.KEYWORD_TYPE`, `kw.SYMBOL_TYPE`
468
+ - `kw.REF`, `kw.BYTES`
469
+ - `kw.ONE`, `kw.MANY`
470
+ - `kw.UNIQUE_VALUE`, `kw.UNIQUE_IDENTITY`
471
+ - `kw.SCHEMA_READ`, `kw.SCHEMA_WRITE`
472
+
473
+ ### Low-Level API
474
+
475
+ **Database Operations:**
476
+ - `create_database(config)` - Create a new database
477
+ - `delete_database(config)` - Delete a database
478
+ - `database_exists(config)` - Check if database exists
479
+
480
+ **Data Operations:**
481
+ - `transact(config, tx_data, input_format='edn')` - Execute transaction
482
+ - `q(query, inputs, output_format='cbor')` - Execute Datalog query
483
+ - `pull(config, selector, eid, output_format='cbor')` - Pull entity
484
+ - `pull_many(config, selector, eids, output_format='cbor')` - Pull multiple
485
+ - `entity(config, eid, output_format='cbor')` - Get entity by ID
486
+
487
+ **Index Operations:**
488
+ - `datoms(config, index, output_format='cbor')` - Get datoms
489
+ - `seek_datoms(config, index, components, output_format='cbor')` - Seek datoms
490
+ - `index_range(config, attr, start, end, output_format='cbor')` - Get range
491
+
492
+ **Schema & Metadata:**
493
+ - `schema(config, output_format='cbor')` - Get schema
494
+ - `reverse_schema(config, output_format='cbor')` - Get reverse schema
495
+ - `metrics(config, output_format='cbor')` - Get metrics
496
+
497
+ **Maintenance:**
498
+ - `gc_storage(config)` - Garbage collect storage
499
+
500
+ ## Development
501
+
502
+ ### Running Tests
503
+
504
+ ```bash
505
+ # Build native library first
506
+ cd datahike && bb ni-compile
507
+
508
+ # Run all tests
509
+ cd pydatahike
510
+ python -m pytest tests/ -v
511
+
512
+ # Run specific test file
513
+ python -m pytest tests/test_edn_conversion.py -v
514
+ ```
515
+
516
+ ### Type Checking
517
+
518
+ ```bash
519
+ pip install mypy
520
+ mypy src/datahike
521
+ ```
522
+
523
+ ## Migration from Low-Level API
524
+
525
+ If you're using the low-level API with EDN strings, migrating to the high-level API is straightforward:
526
+
527
+ **Before:**
528
+ ```python
529
+ from datahike import create_database, transact, q, delete_database
530
+
531
+ config = '{:store {:backend :memory :id "mydb"}}'
532
+ create_database(config)
533
+ transact(config, '[{:name "Alice"}]')
534
+ result = q('[:find ?name :where [?e :name ?name]]', [('db', config)])
535
+ delete_database(config)
536
+ ```
537
+
538
+ **After:**
539
+ ```python
540
+ import uuid
541
+ from datahike import Database
542
+
543
+ db = Database.memory(str(uuid.uuid4()))
544
+ db.create()
545
+ db.transact({"name": "Alice"})
546
+ result = db.q('[:find ?name :where [?e :name ?name]]')
547
+ db.delete()
548
+ ```
549
+
550
+ **Or with context manager:**
551
+ ```python
552
+ import uuid
553
+ from datahike import database
554
+
555
+ with database(backend=':memory', id=str(uuid.uuid4())) as db:
556
+ db.transact({"name": "Alice"})
557
+ result = db.q('[:find ?name :where [?e :name ?name]]')
558
+ ```
559
+
560
+ ## License
561
+
562
+ Eclipse Public License 1.0 (EPL-1.0)
563
+
564
+ ## Links
565
+
566
+ - [Datahike GitHub](https://github.com/replikativ/datahike)
567
+ - [Datahike Documentation](https://github.com/replikativ/datahike/blob/main/doc/index.md)
568
+ - [EDN Conversion Rules](../doc/bindings/edn-conversion.md)
569
+ - [Datalog Tutorial](https://docs.datomic.com/on-prem/query.html)
@@ -0,0 +1,91 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "datahike"
7
+ dynamic = ["version"]
8
+ description = "Python bindings for Datahike - a durable Datalog database"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = {text = "EPL-1.0"}
12
+ authors = [
13
+ {name = "Datahike Team", email = "info@replikativ.io"}
14
+ ]
15
+ keywords = ["datalog", "database", "datomic", "datascript", "query"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: Eclipse Public License 1.0 (EPL-1.0)",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Database",
28
+ "Topic :: Database :: Database Engines/Servers",
29
+ "Typing :: Typed",
30
+ ]
31
+
32
+ dependencies = [
33
+ "cbor2>=5.4.0",
34
+ ]
35
+
36
+ [project.optional-dependencies]
37
+ dev = [
38
+ "pytest>=7.0",
39
+ "pytest-cov>=4.0",
40
+ "mypy>=1.0",
41
+ ]
42
+
43
+ [project.urls]
44
+ Homepage = "https://github.com/replikativ/datahike"
45
+ Documentation = "https://github.com/replikativ/datahike/blob/main/doc/index.md"
46
+ Repository = "https://github.com/replikativ/datahike"
47
+ Issues = "https://github.com/replikativ/datahike/issues"
48
+
49
+ [tool.setuptools]
50
+ package-dir = {"" = "src"}
51
+
52
+ [tool.setuptools.packages.find]
53
+ where = ["src"]
54
+
55
+ [tool.setuptools.package-data]
56
+ datahike = ["py.typed"]
57
+
58
+ [tool.setuptools.dynamic]
59
+ version = {attr = "datahike._version.__version__"}
60
+
61
+ [tool.pytest.ini_options]
62
+ testpaths = ["tests"]
63
+ python_files = ["test_*.py"]
64
+ python_classes = ["Test*"]
65
+ python_functions = ["test_*"]
66
+
67
+ [tool.mypy]
68
+ python_version = "3.8"
69
+ warn_return_any = true
70
+ warn_unused_configs = true
71
+ disallow_untyped_defs = false
72
+ disallow_incomplete_defs = false
73
+ check_untyped_defs = true
74
+ no_implicit_optional = true
75
+ warn_redundant_casts = true
76
+ warn_unused_ignores = true
77
+ warn_no_return = true
78
+
79
+ [tool.coverage.run]
80
+ source = ["src/datahike"]
81
+ omit = ["*/tests/*"]
82
+
83
+ [tool.coverage.report]
84
+ exclude_lines = [
85
+ "pragma: no cover",
86
+ "def __repr__",
87
+ "raise AssertionError",
88
+ "raise NotImplementedError",
89
+ "if __name__ == .__main__.:",
90
+ "if TYPE_CHECKING:",
91
+ ]
@@ -0,0 +1,42 @@
1
+ """Datahike Python bindings setup."""
2
+ from setuptools import setup, find_packages
3
+
4
+ # Read version from _version.py
5
+ version = {}
6
+ with open("src/datahike/_version.py") as f:
7
+ exec(f.read(), version)
8
+
9
+ setup(
10
+ name='datahike',
11
+ version=version['__version__'],
12
+ description="Python bindings for Datahike - a durable Datalog database.",
13
+ long_description=open('README.md').read() if __import__('os').path.exists('README.md') else '',
14
+ long_description_content_type='text/markdown',
15
+ author='Datahike Team',
16
+ author_email='info@replikativ.io',
17
+ url='https://github.com/replikativ/datahike',
18
+ packages=find_packages("src"),
19
+ package_dir={"": "src"},
20
+ python_requires=">=3.8",
21
+ install_requires=[
22
+ 'cbor2>=5.4.0',
23
+ ],
24
+ extras_require={
25
+ 'dev': [
26
+ 'pytest>=7.0',
27
+ 'pytest-cov>=4.0',
28
+ ],
29
+ },
30
+ classifiers=[
31
+ 'Development Status :: 4 - Beta',
32
+ 'Intended Audience :: Developers',
33
+ 'License :: OSI Approved :: Eclipse Public License 1.0 (EPL-1.0)',
34
+ 'Programming Language :: Python :: 3',
35
+ 'Programming Language :: Python :: 3.8',
36
+ 'Programming Language :: Python :: 3.9',
37
+ 'Programming Language :: Python :: 3.10',
38
+ 'Programming Language :: Python :: 3.11',
39
+ 'Programming Language :: Python :: 3.12',
40
+ 'Topic :: Database',
41
+ ],
42
+ )