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.
- package/.circleci/config.yml +405 -0
- package/.circleci/scripts/gen_ci.clj +194 -0
- package/.cirrus.yml +60 -0
- package/.clj-kondo/babashka/sci/config.edn +1 -0
- package/.clj-kondo/babashka/sci/sci/core.clj +9 -0
- package/.clj-kondo/config.edn +95 -0
- package/.dir-locals.el +2 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +68 -0
- package/.github/ISSUE_TEMPLATE/2-feature-request.yml +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +6 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/workflows/native-image.yml +84 -0
- package/LICENSE +203 -0
- package/README.md +273 -0
- package/bb/deps.edn +9 -0
- package/bb/resources/github-fingerprints +3 -0
- package/bb/resources/native-image-tests/run-bb-pod-tests.clj +162 -0
- package/bb/resources/native-image-tests/run-libdatahike-tests +12 -0
- package/bb/resources/native-image-tests/run-native-image-tests +74 -0
- package/bb/resources/native-image-tests/run-python-tests +22 -0
- package/bb/resources/native-image-tests/testconfig.attr-refs.edn +6 -0
- package/bb/resources/native-image-tests/testconfig.edn +5 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/bb/resources/template/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/bb/resources/template/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/bb/resources/template/pom.xml +22 -0
- package/bb/src/tools/build.clj +132 -0
- package/bb/src/tools/clj_kondo.clj +32 -0
- package/bb/src/tools/deploy.clj +26 -0
- package/bb/src/tools/examples.clj +19 -0
- package/bb/src/tools/npm.clj +100 -0
- package/bb/src/tools/python.clj +14 -0
- package/bb/src/tools/release.clj +94 -0
- package/bb/src/tools/test.clj +148 -0
- package/bb/src/tools/version.clj +47 -0
- package/bb.edn +269 -0
- package/benchmark/src/benchmark/cli.clj +195 -0
- package/benchmark/src/benchmark/compare.clj +157 -0
- package/benchmark/src/benchmark/config.clj +316 -0
- package/benchmark/src/benchmark/measure.clj +187 -0
- package/benchmark/src/benchmark/store.clj +190 -0
- package/benchmark/test/benchmark/measure_test.clj +156 -0
- package/build.clj +30 -0
- package/config.edn +49 -0
- package/deps.edn +138 -0
- package/dev/sandbox.clj +82 -0
- package/dev/sandbox.cljs +127 -0
- package/dev/sandbox_benchmarks.clj +27 -0
- package/dev/sandbox_client.clj +87 -0
- package/dev/sandbox_transact_bench.clj +109 -0
- package/dev/user.clj +79 -0
- package/doc/README.md +96 -0
- package/doc/adl/README.md +6 -0
- package/doc/adl/adr-000-adr.org +28 -0
- package/doc/adl/adr-001-attribute-references.org +15 -0
- package/doc/adl/adr-002-build-tooling.org +54 -0
- package/doc/adl/adr-003-db-meta-data.md +52 -0
- package/doc/adl/adr-004-github-flow.md +40 -0
- package/doc/adl/adr-XYZ-template.md +30 -0
- package/doc/adl/index.org +3 -0
- package/doc/assets/datahike-logo.svg +3 -0
- package/doc/assets/datahiking-invoice.org +85 -0
- package/doc/assets/hhtree2.png +0 -0
- package/doc/assets/network_topology.svg +624 -0
- package/doc/assets/perf.png +0 -0
- package/doc/assets/schema_mindmap.mm +132 -0
- package/doc/assets/schema_mindmap.svg +970 -0
- package/doc/assets/temporal_index.mm +74 -0
- package/doc/backend-development.md +78 -0
- package/doc/bb-pod.md +89 -0
- package/doc/benchmarking.md +360 -0
- package/doc/bindings/edn-conversion.md +383 -0
- package/doc/cli.md +162 -0
- package/doc/cljdoc.edn +27 -0
- package/doc/cljs-support.md +133 -0
- package/doc/config.md +406 -0
- package/doc/contributing.md +114 -0
- package/doc/datalog-vs-sql.md +210 -0
- package/doc/datomic_differences.md +109 -0
- package/doc/development/pull-api-ns.md +186 -0
- package/doc/development/pull-frame-state-diagram.jpg +0 -0
- package/doc/distributed.md +566 -0
- package/doc/entity_spec.md +92 -0
- package/doc/gc.md +273 -0
- package/doc/java-api.md +808 -0
- package/doc/javascript-api.md +421 -0
- package/doc/libdatahike.md +86 -0
- package/doc/logging_and_error_handling.md +43 -0
- package/doc/norms.md +66 -0
- package/doc/schema-migration.md +85 -0
- package/doc/schema.md +287 -0
- package/doc/storage-backends.md +363 -0
- package/doc/store-id-refactoring.md +596 -0
- package/doc/time_variance.md +325 -0
- package/doc/unstructured.md +167 -0
- package/doc/versioning.md +261 -0
- package/examples/basic/README.md +19 -0
- package/examples/basic/deps.edn +6 -0
- package/examples/basic/docker-compose.yml +13 -0
- package/examples/basic/src/examples/core.clj +60 -0
- package/examples/basic/src/examples/schema.clj +155 -0
- package/examples/basic/src/examples/store.clj +60 -0
- package/examples/basic/src/examples/time_travel.clj +185 -0
- package/examples/java/.settings/org.eclipse.core.resources.prefs +3 -0
- package/examples/java/.settings/org.eclipse.jdt.apt.core.prefs +2 -0
- package/examples/java/.settings/org.eclipse.jdt.core.prefs +9 -0
- package/examples/java/.settings/org.eclipse.m2e.core.prefs +4 -0
- package/examples/java/README.md +162 -0
- package/examples/java/pom.xml +62 -0
- package/examples/java/src/main/java/examples/QuickStart.java +115 -0
- package/examples/java/src/main/java/examples/SchemaExample.java +148 -0
- package/examples/java/src/main/java/examples/TimeTravelExample.java +121 -0
- package/flake.lock +27 -0
- package/flake.nix +27 -0
- package/http-server/datahike/http/middleware.clj +75 -0
- package/http-server/datahike/http/server.clj +269 -0
- package/java/src/datahike/java/Database.java +274 -0
- package/java/src/datahike/java/Datahike.java +281 -0
- package/java/src/datahike/java/DatahikeGeneratedTest.java +349 -0
- package/java/src/datahike/java/DatahikeTest.java +370 -0
- package/java/src/datahike/java/EDN.java +170 -0
- package/java/src/datahike/java/IEntity.java +11 -0
- package/java/src/datahike/java/Keywords.java +161 -0
- package/java/src/datahike/java/SchemaFlexibility.java +52 -0
- package/java/src/datahike/java/Util.java +219 -0
- package/karma.conf.js +19 -0
- package/libdatahike/compile-cpp +7 -0
- package/libdatahike/src/datahike/impl/LibDatahikeBase.java +203 -0
- package/libdatahike/src/datahike/impl/libdatahike.clj +59 -0
- package/libdatahike/src/test_cpp.cpp +61 -0
- package/npm-package/PUBLISHING.md +140 -0
- package/npm-package/README.md +226 -0
- package/npm-package/package.template.json +34 -0
- package/npm-package/test-isomorphic.ts +281 -0
- package/npm-package/test.js +557 -0
- package/npm-package/typescript-test.ts +70 -0
- package/package.json +16 -0
- package/pydatahike/README.md +569 -0
- package/pydatahike/pyproject.toml +91 -0
- package/pydatahike/setup.py +42 -0
- package/pydatahike/src/datahike/__init__.py +134 -0
- package/pydatahike/src/datahike/_native.py +250 -0
- package/pydatahike/src/datahike/_version.py +2 -0
- package/pydatahike/src/datahike/database.py +722 -0
- package/pydatahike/src/datahike/edn.py +311 -0
- package/pydatahike/src/datahike/py.typed +0 -0
- package/pydatahike/tests/conftest.py +17 -0
- package/pydatahike/tests/test_basic.py +170 -0
- package/pydatahike/tests/test_database.py +51 -0
- package/pydatahike/tests/test_edn_conversion.py +299 -0
- package/pydatahike/tests/test_query.py +99 -0
- package/pydatahike/tests/test_schema.py +55 -0
- package/resources/clj-kondo.exports/io.replikativ/datahike/config.edn +5 -0
- package/resources/example_server.edn +4 -0
- package/shadow-cljs.edn +56 -0
- package/src/data_readers.clj +7 -0
- package/src/datahike/api/impl.cljc +176 -0
- package/src/datahike/api/specification.cljc +633 -0
- package/src/datahike/api/types.cljc +261 -0
- package/src/datahike/api.cljc +41 -0
- package/src/datahike/array.cljc +99 -0
- package/src/datahike/cli.clj +166 -0
- package/src/datahike/cljs.cljs +6 -0
- package/src/datahike/codegen/cli.clj +406 -0
- package/src/datahike/codegen/clj_kondo.clj +291 -0
- package/src/datahike/codegen/java.clj +403 -0
- package/src/datahike/codegen/naming.cljc +33 -0
- package/src/datahike/codegen/native.clj +559 -0
- package/src/datahike/codegen/pod.clj +488 -0
- package/src/datahike/codegen/python.clj +838 -0
- package/src/datahike/codegen/report.clj +55 -0
- package/src/datahike/codegen/typescript.clj +262 -0
- package/src/datahike/codegen/validation.clj +145 -0
- package/src/datahike/config.cljc +294 -0
- package/src/datahike/connections.cljc +16 -0
- package/src/datahike/connector.cljc +265 -0
- package/src/datahike/constants.cljc +142 -0
- package/src/datahike/core.cljc +297 -0
- package/src/datahike/datom.cljc +459 -0
- package/src/datahike/db/interface.cljc +119 -0
- package/src/datahike/db/search.cljc +305 -0
- package/src/datahike/db/transaction.cljc +937 -0
- package/src/datahike/db/utils.cljc +338 -0
- package/src/datahike/db.cljc +956 -0
- package/src/datahike/experimental/unstructured.cljc +126 -0
- package/src/datahike/experimental/versioning.cljc +172 -0
- package/src/datahike/externs.js +31 -0
- package/src/datahike/gc.cljc +69 -0
- package/src/datahike/http/client.clj +188 -0
- package/src/datahike/http/writer.clj +79 -0
- package/src/datahike/impl/entity.cljc +218 -0
- package/src/datahike/index/interface.cljc +93 -0
- package/src/datahike/index/persistent_set.cljc +469 -0
- package/src/datahike/index/utils.cljc +44 -0
- package/src/datahike/index.cljc +32 -0
- package/src/datahike/js/api.cljs +172 -0
- package/src/datahike/js/api_macros.clj +22 -0
- package/src/datahike/js.cljs +163 -0
- package/src/datahike/json.cljc +209 -0
- package/src/datahike/lru.cljc +146 -0
- package/src/datahike/migrate.clj +39 -0
- package/src/datahike/norm/norm.clj +245 -0
- package/src/datahike/online_gc.cljc +252 -0
- package/src/datahike/pod.clj +155 -0
- package/src/datahike/pull_api.cljc +325 -0
- package/src/datahike/query.cljc +1945 -0
- package/src/datahike/query_stats.cljc +88 -0
- package/src/datahike/readers.cljc +62 -0
- package/src/datahike/remote.cljc +218 -0
- package/src/datahike/schema.cljc +228 -0
- package/src/datahike/schema_cache.cljc +42 -0
- package/src/datahike/spec.cljc +101 -0
- package/src/datahike/store.cljc +80 -0
- package/src/datahike/tools.cljc +308 -0
- package/src/datahike/transit.cljc +80 -0
- package/src/datahike/writer.cljc +239 -0
- package/src/datahike/writing.cljc +362 -0
- package/src/deps.cljs +1 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/insert.cljc +76 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree/upsert.cljc +128 -0
- package/src-hitchhiker-tree/datahike/index/hitchhiker_tree.cljc +213 -0
- package/test/datahike/backward_compatibility_test/src/backward_test.clj +37 -0
- package/test/datahike/integration_test/config_record_file_test.clj +14 -0
- package/test/datahike/integration_test/config_record_test.clj +14 -0
- package/test/datahike/integration_test/depr_config_uri_test.clj +15 -0
- package/test/datahike/integration_test/return_map_test.clj +62 -0
- package/test/datahike/integration_test.cljc +67 -0
- package/test/datahike/norm/norm_test.clj +124 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/01-transact-basic-characters.edn +2 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/02 add occupation.edn +5 -0
- package/test/datahike/norm/resources/naming-and-sorting-test/checksums.edn +12 -0
- package/test/datahike/norm/resources/simple-test/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/simple-test/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/first/checksums.edn +6 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/004-tx-data-and-tx-fn-test.edn +5 -0
- package/test/datahike/norm/resources/tx-data-and-tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/norm/resources/tx-fn-test/first/001-a1-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/002-a2-example.edn +5 -0
- package/test/datahike/norm/resources/tx-fn-test/first/checksums.edn +4 -0
- package/test/datahike/norm/resources/tx-fn-test/second/003-tx-fn-test.edn +1 -0
- package/test/datahike/norm/resources/tx-fn-test/second/checksums.edn +2 -0
- package/test/datahike/test/api_test.cljc +895 -0
- package/test/datahike/test/array_test.cljc +40 -0
- package/test/datahike/test/attribute_refs/datoms_test.cljc +140 -0
- package/test/datahike/test/attribute_refs/db_test.cljc +42 -0
- package/test/datahike/test/attribute_refs/differences_test.cljc +515 -0
- package/test/datahike/test/attribute_refs/entity_test.cljc +89 -0
- package/test/datahike/test/attribute_refs/pull_api_test.cljc +320 -0
- package/test/datahike/test/attribute_refs/query_find_specs_test.cljc +59 -0
- package/test/datahike/test/attribute_refs/query_fns_test.cljc +130 -0
- package/test/datahike/test/attribute_refs/query_interop_test.cljc +47 -0
- package/test/datahike/test/attribute_refs/query_not_test.cljc +193 -0
- package/test/datahike/test/attribute_refs/query_or_test.cljc +137 -0
- package/test/datahike/test/attribute_refs/query_pull_test.cljc +156 -0
- package/test/datahike/test/attribute_refs/query_rules_test.cljc +176 -0
- package/test/datahike/test/attribute_refs/query_test.cljc +241 -0
- package/test/datahike/test/attribute_refs/temporal_search.cljc +22 -0
- package/test/datahike/test/attribute_refs/transact_test.cljc +220 -0
- package/test/datahike/test/attribute_refs/utils.cljc +128 -0
- package/test/datahike/test/cache_test.cljc +38 -0
- package/test/datahike/test/components_test.cljc +92 -0
- package/test/datahike/test/config_test.cljc +158 -0
- package/test/datahike/test/core_test.cljc +105 -0
- package/test/datahike/test/datom_test.cljc +44 -0
- package/test/datahike/test/db_test.cljc +54 -0
- package/test/datahike/test/entity_spec_test.cljc +159 -0
- package/test/datahike/test/entity_test.cljc +103 -0
- package/test/datahike/test/explode_test.cljc +143 -0
- package/test/datahike/test/filter_test.cljc +75 -0
- package/test/datahike/test/gc_test.cljc +159 -0
- package/test/datahike/test/http/server_test.clj +192 -0
- package/test/datahike/test/http/writer_test.clj +86 -0
- package/test/datahike/test/ident_test.cljc +32 -0
- package/test/datahike/test/index_test.cljc +345 -0
- package/test/datahike/test/insert.cljc +125 -0
- package/test/datahike/test/java_bindings_test.clj +6 -0
- package/test/datahike/test/listen_test.cljc +41 -0
- package/test/datahike/test/lookup_refs_test.cljc +266 -0
- package/test/datahike/test/lru_test.cljc +27 -0
- package/test/datahike/test/migrate_test.clj +297 -0
- package/test/datahike/test/model/core.cljc +376 -0
- package/test/datahike/test/model/invariant.cljc +142 -0
- package/test/datahike/test/model/rng.cljc +82 -0
- package/test/datahike/test/model_test.clj +217 -0
- package/test/datahike/test/nodejs_test.cljs +262 -0
- package/test/datahike/test/online_gc_test.cljc +475 -0
- package/test/datahike/test/pod_test.clj +369 -0
- package/test/datahike/test/pull_api_test.cljc +474 -0
- package/test/datahike/test/purge_test.cljc +144 -0
- package/test/datahike/test/query_aggregates_test.cljc +101 -0
- package/test/datahike/test/query_find_specs_test.cljc +52 -0
- package/test/datahike/test/query_fns_test.cljc +523 -0
- package/test/datahike/test/query_interop_test.cljc +47 -0
- package/test/datahike/test/query_not_test.cljc +189 -0
- package/test/datahike/test/query_or_test.cljc +158 -0
- package/test/datahike/test/query_pull_test.cljc +147 -0
- package/test/datahike/test/query_rules_test.cljc +248 -0
- package/test/datahike/test/query_stats_test.cljc +218 -0
- package/test/datahike/test/query_test.cljc +984 -0
- package/test/datahike/test/schema_test.cljc +424 -0
- package/test/datahike/test/specification_test.cljc +30 -0
- package/test/datahike/test/store_test.cljc +78 -0
- package/test/datahike/test/stress_test.cljc +57 -0
- package/test/datahike/test/time_variance_test.cljc +518 -0
- package/test/datahike/test/tools_test.clj +134 -0
- package/test/datahike/test/transact_test.cljc +518 -0
- package/test/datahike/test/tuples_test.cljc +564 -0
- package/test/datahike/test/unstructured_test.cljc +291 -0
- package/test/datahike/test/upsert_impl_test.cljc +205 -0
- package/test/datahike/test/upsert_test.cljc +363 -0
- package/test/datahike/test/utils.cljc +110 -0
- package/test/datahike/test/validation_test.cljc +48 -0
- package/test/datahike/test/versioning_test.cljc +56 -0
- package/test/datahike/test.cljc +66 -0
- package/tests.edn +24 -0
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
(ns datahike.codegen.python
|
|
2
|
+
"Generate Python bindings from api.specification.
|
|
3
|
+
|
|
4
|
+
This namespace generates Python code with:
|
|
5
|
+
- Full type annotations (PEP 484)
|
|
6
|
+
- Docstrings from specification
|
|
7
|
+
- ctypes FFI calls to libdatahike"
|
|
8
|
+
(:require [datahike.api.specification :refer [api-specification]]
|
|
9
|
+
[datahike.codegen.naming :as naming]
|
|
10
|
+
[datahike.codegen.validation :as validation]
|
|
11
|
+
[clojure.string :as str]
|
|
12
|
+
[clojure.java.io :as io]))
|
|
13
|
+
|
|
14
|
+
;; =============================================================================
|
|
15
|
+
;; Python Operation Configuration
|
|
16
|
+
;; =============================================================================
|
|
17
|
+
|
|
18
|
+
(def python-operations
|
|
19
|
+
"Operations exposed via Python API.
|
|
20
|
+
Maps operation names to their binding configuration."
|
|
21
|
+
'{;; Database lifecycle
|
|
22
|
+
database-exists?
|
|
23
|
+
{:pattern :config-query
|
|
24
|
+
:return-type "bool"}
|
|
25
|
+
|
|
26
|
+
create-database
|
|
27
|
+
{:pattern :config-mutation
|
|
28
|
+
:return-type "None"}
|
|
29
|
+
|
|
30
|
+
delete-database
|
|
31
|
+
{:pattern :config-mutation
|
|
32
|
+
:return-type "None"}
|
|
33
|
+
|
|
34
|
+
;; Query
|
|
35
|
+
q
|
|
36
|
+
{:pattern :query
|
|
37
|
+
:return-type "Any"}
|
|
38
|
+
|
|
39
|
+
;; Transaction
|
|
40
|
+
transact
|
|
41
|
+
{:pattern :transact
|
|
42
|
+
:return-type "Dict[str, Any]"}
|
|
43
|
+
|
|
44
|
+
;; Pull API
|
|
45
|
+
pull
|
|
46
|
+
{:pattern :db-selector-eid
|
|
47
|
+
:return-type "Optional[Dict[str, Any]]"}
|
|
48
|
+
|
|
49
|
+
pull-many
|
|
50
|
+
{:pattern :db-selector-eids
|
|
51
|
+
:return-type "List[Dict[str, Any]]"}
|
|
52
|
+
|
|
53
|
+
;; Entity
|
|
54
|
+
entity
|
|
55
|
+
{:pattern :db-eid
|
|
56
|
+
:return-type "Dict[str, Any]"}
|
|
57
|
+
|
|
58
|
+
;; Index operations
|
|
59
|
+
datoms
|
|
60
|
+
{:pattern :db-index
|
|
61
|
+
:return-type "List[List[Any]]"}
|
|
62
|
+
|
|
63
|
+
seek-datoms
|
|
64
|
+
{:pattern :db-index
|
|
65
|
+
:c-name "seek_datoms"
|
|
66
|
+
:return-type "List[List[Any]]"}
|
|
67
|
+
|
|
68
|
+
index-range
|
|
69
|
+
{:pattern :db-index-range
|
|
70
|
+
:c-name "index_range"
|
|
71
|
+
:return-type "List[List[Any]]"}
|
|
72
|
+
|
|
73
|
+
;; Schema
|
|
74
|
+
schema
|
|
75
|
+
{:pattern :db-only
|
|
76
|
+
:return-type "Dict[str, Any]"}
|
|
77
|
+
|
|
78
|
+
reverse-schema
|
|
79
|
+
{:pattern :db-only
|
|
80
|
+
:c-name "reverse_schema"
|
|
81
|
+
:return-type "Dict[str, Any]"}
|
|
82
|
+
|
|
83
|
+
;; Diagnostics
|
|
84
|
+
metrics
|
|
85
|
+
{:pattern :db-only
|
|
86
|
+
:return-type "Dict[str, Any]"}
|
|
87
|
+
|
|
88
|
+
;; Maintenance
|
|
89
|
+
gc-storage
|
|
90
|
+
{:pattern :config-timestamp
|
|
91
|
+
:c-name "gc_storage"
|
|
92
|
+
:return-type "Any"}})
|
|
93
|
+
|
|
94
|
+
(def python-excluded-operations
|
|
95
|
+
"Operations explicitly excluded from Python FFI bindings with documented reasons.
|
|
96
|
+
|
|
97
|
+
Each entry maps operation symbol to exclusion reason string.
|
|
98
|
+
Same exclusions as Native bindings since both use libdatahike FFI."
|
|
99
|
+
'{connect "Connection lifecycle managed internally per FFI call"
|
|
100
|
+
release "Connections automatically released after each operation"
|
|
101
|
+
db "Database dereferencing handled internally via input_format parameter"
|
|
102
|
+
listen "Requires persistent callbacks across FFI boundary - not supported"
|
|
103
|
+
unlisten "Requires persistent callbacks across FFI boundary - not supported"
|
|
104
|
+
as-of "Returns DB object - use input_format='asof:timestamp_ms' instead"
|
|
105
|
+
since "Returns DB object - use input_format='since:timestamp_ms' instead"
|
|
106
|
+
history "Returns DB object - use input_format='history' instead"
|
|
107
|
+
filter "Returns DB object - cannot be serialized across FFI boundary"
|
|
108
|
+
with "Pure function better implemented client-side"
|
|
109
|
+
db-with "Pure function better implemented client-side"
|
|
110
|
+
tempid "Temporary IDs only useful within transaction context"
|
|
111
|
+
entity-db "Returns DB from entity - limited utility in FFI context"
|
|
112
|
+
is-filtered "Returns boolean about DB state - limited utility in FFI context"
|
|
113
|
+
transact! "Alias for transact - only need one binding"
|
|
114
|
+
load-entities "Batch loading better done via repeated transact calls"
|
|
115
|
+
query-stats "Query statistics not yet exposed in Python FFI"})
|
|
116
|
+
|
|
117
|
+
;; =============================================================================
|
|
118
|
+
;; Type Derivation: Malli → Python
|
|
119
|
+
;; =============================================================================
|
|
120
|
+
|
|
121
|
+
(defn malli->python-type
|
|
122
|
+
"Convert Malli schema to Python type annotation string.
|
|
123
|
+
|
|
124
|
+
Handles common Malli schemas and semantic types from api.specification.
|
|
125
|
+
Falls back to 'Any' for complex or unknown schemas."
|
|
126
|
+
[schema]
|
|
127
|
+
(cond
|
|
128
|
+
;; Keyword schemas (primitives)
|
|
129
|
+
(keyword? schema)
|
|
130
|
+
(case schema
|
|
131
|
+
:boolean "bool"
|
|
132
|
+
:string "str"
|
|
133
|
+
:int "int"
|
|
134
|
+
:long "int"
|
|
135
|
+
:double "float"
|
|
136
|
+
:number "float"
|
|
137
|
+
:keyword "str"
|
|
138
|
+
:symbol "str"
|
|
139
|
+
:any "Any"
|
|
140
|
+
:nil "None"
|
|
141
|
+
:map "Dict[str, Any]"
|
|
142
|
+
:vector "List[Any]"
|
|
143
|
+
:sequential "List[Any]"
|
|
144
|
+
:set "Set[Any]"
|
|
145
|
+
"Any")
|
|
146
|
+
|
|
147
|
+
;; Symbol schemas (type references from api.types)
|
|
148
|
+
(symbol? schema)
|
|
149
|
+
(let [schema-name (name schema)]
|
|
150
|
+
(case schema-name
|
|
151
|
+
;; Semantic Datahike types
|
|
152
|
+
"SConnection" "Any" ; Connection not exposed in FFI
|
|
153
|
+
"SDB" "Any" ; DB objects handled via input_format
|
|
154
|
+
"SEntity" "Dict[str, Any]"
|
|
155
|
+
"STransactionReport" "Dict[str, Any]"
|
|
156
|
+
"SSchema" "Dict[str, Any]"
|
|
157
|
+
"SMetrics" "Dict[str, Any]"
|
|
158
|
+
"SDatoms" "List[List[Any]]"
|
|
159
|
+
"SEId" "int"
|
|
160
|
+
"SPullPattern" "str"
|
|
161
|
+
"SConfig" "str" ; EDN string in Python FFI
|
|
162
|
+
"STransactions" "str" ; EDN/JSON string
|
|
163
|
+
"SQueryArgs" "Any"
|
|
164
|
+
;; Default
|
|
165
|
+
"Any"))
|
|
166
|
+
|
|
167
|
+
;; Vector schemas (compound types)
|
|
168
|
+
(vector? schema)
|
|
169
|
+
(let [[op & args] schema]
|
|
170
|
+
(case op
|
|
171
|
+
;; [:or Type1 Type2] → Union[Type1, Type2] or Any if too complex
|
|
172
|
+
:or
|
|
173
|
+
(let [python-types (map malli->python-type args)]
|
|
174
|
+
(if (every? #(not= % "Any") python-types)
|
|
175
|
+
(str "Union[" (str/join ", " python-types) "]")
|
|
176
|
+
"Any"))
|
|
177
|
+
|
|
178
|
+
;; [:maybe Type] → Optional[Type]
|
|
179
|
+
:maybe
|
|
180
|
+
(str "Optional[" (malli->python-type (first args)) "]")
|
|
181
|
+
|
|
182
|
+
;; [:sequential Type] → List[Type]
|
|
183
|
+
:sequential
|
|
184
|
+
(str "List[" (malli->python-type (first args)) "]")
|
|
185
|
+
|
|
186
|
+
;; [:vector Type] → List[Type]
|
|
187
|
+
:vector
|
|
188
|
+
(str "List[" (malli->python-type (first args)) "]")
|
|
189
|
+
|
|
190
|
+
;; [:set Type] → Set[Type]
|
|
191
|
+
:set
|
|
192
|
+
(str "Set[" (malli->python-type (first args)) "]")
|
|
193
|
+
|
|
194
|
+
;; [:map ...] → Dict
|
|
195
|
+
:map "Dict[str, Any]"
|
|
196
|
+
|
|
197
|
+
;; [:function ...] or [:=> ...] - extract return type
|
|
198
|
+
(:function :=>)
|
|
199
|
+
(if (= op :=>)
|
|
200
|
+
(malli->python-type (nth schema 2)) ; [:=> input output]
|
|
201
|
+
(malli->python-type (second schema))) ; [:function [:=> ...]]
|
|
202
|
+
|
|
203
|
+
;; Default
|
|
204
|
+
"Any"))
|
|
205
|
+
|
|
206
|
+
;; Default
|
|
207
|
+
:else "Any"))
|
|
208
|
+
|
|
209
|
+
;; =============================================================================
|
|
210
|
+
;; Naming Conventions
|
|
211
|
+
;; =============================================================================
|
|
212
|
+
|
|
213
|
+
(defn clj-name->python-name
|
|
214
|
+
"Convert Clojure kebab-case to Python snake_case."
|
|
215
|
+
[op-name]
|
|
216
|
+
(-> (name op-name)
|
|
217
|
+
(str/replace #"[!?]$" "")
|
|
218
|
+
(str/replace #"-" "_")))
|
|
219
|
+
|
|
220
|
+
(defn get-c-name
|
|
221
|
+
"Get C function name for FFI call."
|
|
222
|
+
[op-name config]
|
|
223
|
+
(or (:c-name config) (clj-name->python-name op-name)))
|
|
224
|
+
|
|
225
|
+
;; =============================================================================
|
|
226
|
+
;; Documentation Formatting
|
|
227
|
+
;; =============================================================================
|
|
228
|
+
|
|
229
|
+
(defn format-python-docstring
|
|
230
|
+
"Format operation documentation as Python docstring.
|
|
231
|
+
|
|
232
|
+
Includes:
|
|
233
|
+
- Main documentation text
|
|
234
|
+
- Examples from specification (kept as Clojure syntax)
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
doc - Documentation string from api-specification
|
|
238
|
+
examples - Example vector from api-specification
|
|
239
|
+
|
|
240
|
+
Returns formatted Python docstring"
|
|
241
|
+
[doc examples]
|
|
242
|
+
(let [main-doc (or doc "No documentation available.")
|
|
243
|
+
example-text (when (seq examples)
|
|
244
|
+
(str "\n\n Examples:\n"
|
|
245
|
+
(str/join "\n"
|
|
246
|
+
(for [{:keys [desc code]} examples]
|
|
247
|
+
(str " " desc ":\n"
|
|
248
|
+
" " code)))))]
|
|
249
|
+
(str main-doc example-text)))
|
|
250
|
+
|
|
251
|
+
;; =============================================================================
|
|
252
|
+
;; Code Generation Templates
|
|
253
|
+
;; =============================================================================
|
|
254
|
+
|
|
255
|
+
(defn generate-config-query
|
|
256
|
+
"Generate Python function for config-based query operations."
|
|
257
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
258
|
+
(let [py-name (clj-name->python-name op-name)
|
|
259
|
+
c-name (get-c-name op-name config)]
|
|
260
|
+
(format "
|
|
261
|
+
def %s(
|
|
262
|
+
config: str,
|
|
263
|
+
output_format: str = 'cbor'
|
|
264
|
+
) -> %s:
|
|
265
|
+
'''%s
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
config: Database configuration as EDN string
|
|
269
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
%s
|
|
273
|
+
'''
|
|
274
|
+
callback, get_result = make_callback(output_format)
|
|
275
|
+
get_dll().%s(
|
|
276
|
+
get_isolatethread(),
|
|
277
|
+
config.encode('utf8'),
|
|
278
|
+
output_format.encode('utf8'),
|
|
279
|
+
callback
|
|
280
|
+
)
|
|
281
|
+
return get_result()
|
|
282
|
+
"
|
|
283
|
+
py-name return-type doc return-type c-name)))
|
|
284
|
+
|
|
285
|
+
(defn generate-config-mutation
|
|
286
|
+
"Generate Python function for config-based mutation operations."
|
|
287
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
288
|
+
(let [py-name (clj-name->python-name op-name)
|
|
289
|
+
c-name (get-c-name op-name config)]
|
|
290
|
+
(format "
|
|
291
|
+
def %s(
|
|
292
|
+
config: str,
|
|
293
|
+
output_format: str = 'cbor'
|
|
294
|
+
) -> %s:
|
|
295
|
+
'''%s
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
config: Database configuration as EDN string
|
|
299
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
300
|
+
'''
|
|
301
|
+
callback, get_result = make_callback(output_format)
|
|
302
|
+
get_dll().%s(
|
|
303
|
+
get_isolatethread(),
|
|
304
|
+
config.encode('utf8'),
|
|
305
|
+
output_format.encode('utf8'),
|
|
306
|
+
callback
|
|
307
|
+
)
|
|
308
|
+
get_result() # Check for exceptions
|
|
309
|
+
"
|
|
310
|
+
py-name return-type doc c-name)))
|
|
311
|
+
|
|
312
|
+
(defn generate-query
|
|
313
|
+
"Generate Python function for query operation."
|
|
314
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
315
|
+
(let [py-name (clj-name->python-name op-name)
|
|
316
|
+
c-name (get-c-name op-name config)]
|
|
317
|
+
(format "
|
|
318
|
+
def %s(
|
|
319
|
+
query: str,
|
|
320
|
+
inputs: List[Tuple[str, str]],
|
|
321
|
+
output_format: str = 'cbor'
|
|
322
|
+
) -> %s:
|
|
323
|
+
'''%s
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
query: Datalog query as EDN string
|
|
327
|
+
inputs: List of (format, value) tuples. Formats:
|
|
328
|
+
- 'db': Current database (value is config EDN)
|
|
329
|
+
- 'history': Full history database
|
|
330
|
+
- 'since:{timestamp_ms}': Database since timestamp
|
|
331
|
+
- 'asof:{timestamp_ms}': Database as-of timestamp
|
|
332
|
+
- 'json': JSON data
|
|
333
|
+
- 'edn': EDN data
|
|
334
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
Query result
|
|
338
|
+
|
|
339
|
+
Example:
|
|
340
|
+
>>> query('[:find ?e :where [?e :name \"Alice\"]]', [('db', config)])
|
|
341
|
+
'''
|
|
342
|
+
n, formats, values = prepare_query_inputs(inputs)
|
|
343
|
+
callback, get_result = make_callback(output_format)
|
|
344
|
+
get_dll().%s(
|
|
345
|
+
get_isolatethread(),
|
|
346
|
+
query.encode('utf8'),
|
|
347
|
+
n,
|
|
348
|
+
formats,
|
|
349
|
+
values,
|
|
350
|
+
output_format.encode('utf8'),
|
|
351
|
+
callback
|
|
352
|
+
)
|
|
353
|
+
return get_result()
|
|
354
|
+
"
|
|
355
|
+
py-name return-type doc c-name)))
|
|
356
|
+
|
|
357
|
+
(defn generate-transact
|
|
358
|
+
"Generate Python function for transact operation."
|
|
359
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
360
|
+
(let [py-name (clj-name->python-name op-name)
|
|
361
|
+
c-name (get-c-name op-name config)]
|
|
362
|
+
(format "
|
|
363
|
+
def %s(
|
|
364
|
+
config: str,
|
|
365
|
+
tx_data: str,
|
|
366
|
+
input_format: str = 'json',
|
|
367
|
+
output_format: str = 'cbor'
|
|
368
|
+
) -> %s:
|
|
369
|
+
'''%s
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
config: Database configuration as EDN string
|
|
373
|
+
tx_data: Transaction data (format depends on input_format)
|
|
374
|
+
input_format: Input data format ('json', 'edn', or 'cbor')
|
|
375
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Transaction metadata
|
|
379
|
+
|
|
380
|
+
Example:
|
|
381
|
+
>>> transact(config, '[{\"name\": \"Alice\", \"age\": 30}]')
|
|
382
|
+
'''
|
|
383
|
+
callback, get_result = make_callback(output_format)
|
|
384
|
+
get_dll().%s(
|
|
385
|
+
get_isolatethread(),
|
|
386
|
+
config.encode('utf8'),
|
|
387
|
+
input_format.encode('utf8'),
|
|
388
|
+
tx_data.encode('utf8'),
|
|
389
|
+
output_format.encode('utf8'),
|
|
390
|
+
callback
|
|
391
|
+
)
|
|
392
|
+
return get_result()
|
|
393
|
+
"
|
|
394
|
+
py-name return-type doc c-name)))
|
|
395
|
+
|
|
396
|
+
(defn generate-db-only
|
|
397
|
+
"Generate Python function for db-only operations."
|
|
398
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
399
|
+
(let [py-name (clj-name->python-name op-name)
|
|
400
|
+
c-name (get-c-name op-name config)]
|
|
401
|
+
(format "
|
|
402
|
+
def %s(
|
|
403
|
+
config: str,
|
|
404
|
+
input_format: str = 'db',
|
|
405
|
+
output_format: str = 'cbor'
|
|
406
|
+
) -> %s:
|
|
407
|
+
'''%s
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
config: Database configuration as EDN string
|
|
411
|
+
input_format: Database input format ('db', 'history', 'since:ts', 'asof:ts')
|
|
412
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
%s
|
|
416
|
+
'''
|
|
417
|
+
callback, get_result = make_callback(output_format)
|
|
418
|
+
get_dll().%s(
|
|
419
|
+
get_isolatethread(),
|
|
420
|
+
input_format.encode('utf8'),
|
|
421
|
+
config.encode('utf8'),
|
|
422
|
+
output_format.encode('utf8'),
|
|
423
|
+
callback
|
|
424
|
+
)
|
|
425
|
+
return get_result()
|
|
426
|
+
"
|
|
427
|
+
py-name return-type doc return-type c-name)))
|
|
428
|
+
|
|
429
|
+
(defn generate-db-eid
|
|
430
|
+
"Generate Python function for db + eid operations."
|
|
431
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
432
|
+
(let [py-name (clj-name->python-name op-name)
|
|
433
|
+
c-name (get-c-name op-name config)]
|
|
434
|
+
(format "
|
|
435
|
+
def %s(
|
|
436
|
+
config: str,
|
|
437
|
+
eid: int,
|
|
438
|
+
input_format: str = 'db',
|
|
439
|
+
output_format: str = 'cbor'
|
|
440
|
+
) -> %s:
|
|
441
|
+
'''%s
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
config: Database configuration as EDN string
|
|
445
|
+
eid: Entity ID
|
|
446
|
+
input_format: Database input format ('db', 'history', 'since:ts', 'asof:ts')
|
|
447
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
%s
|
|
451
|
+
'''
|
|
452
|
+
callback, get_result = make_callback(output_format)
|
|
453
|
+
get_dll().%s(
|
|
454
|
+
get_isolatethread(),
|
|
455
|
+
input_format.encode('utf8'),
|
|
456
|
+
config.encode('utf8'),
|
|
457
|
+
eid,
|
|
458
|
+
output_format.encode('utf8'),
|
|
459
|
+
callback
|
|
460
|
+
)
|
|
461
|
+
return get_result()
|
|
462
|
+
"
|
|
463
|
+
py-name return-type doc return-type c-name)))
|
|
464
|
+
|
|
465
|
+
(defn generate-db-selector-eid
|
|
466
|
+
"Generate Python function for pull operation."
|
|
467
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
468
|
+
(let [py-name (clj-name->python-name op-name)
|
|
469
|
+
c-name (get-c-name op-name config)]
|
|
470
|
+
(format "
|
|
471
|
+
def %s(
|
|
472
|
+
config: str,
|
|
473
|
+
selector: str,
|
|
474
|
+
eid: int,
|
|
475
|
+
input_format: str = 'db',
|
|
476
|
+
output_format: str = 'cbor'
|
|
477
|
+
) -> %s:
|
|
478
|
+
'''%s
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
config: Database configuration as EDN string
|
|
482
|
+
selector: Pull pattern as EDN string (e.g., '[:db/id :name :age]' or '[*]')
|
|
483
|
+
eid: Entity ID
|
|
484
|
+
input_format: Database input format ('db', 'history', 'since:ts', 'asof:ts')
|
|
485
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
%s
|
|
489
|
+
|
|
490
|
+
Example:
|
|
491
|
+
>>> pull(config, '[*]', 1)
|
|
492
|
+
{':db/id': 1, ':name': 'Alice', ':age': 30}
|
|
493
|
+
'''
|
|
494
|
+
callback, get_result = make_callback(output_format)
|
|
495
|
+
get_dll().%s(
|
|
496
|
+
get_isolatethread(),
|
|
497
|
+
input_format.encode('utf8'),
|
|
498
|
+
config.encode('utf8'),
|
|
499
|
+
selector.encode('utf8'),
|
|
500
|
+
eid,
|
|
501
|
+
output_format.encode('utf8'),
|
|
502
|
+
callback
|
|
503
|
+
)
|
|
504
|
+
return get_result()
|
|
505
|
+
"
|
|
506
|
+
py-name return-type doc return-type c-name)))
|
|
507
|
+
|
|
508
|
+
(defn generate-db-selector-eids
|
|
509
|
+
"Generate Python function for pull-many operation."
|
|
510
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
511
|
+
(let [py-name (clj-name->python-name op-name)
|
|
512
|
+
c-name (get-c-name op-name config)]
|
|
513
|
+
(format "
|
|
514
|
+
def %s(
|
|
515
|
+
config: str,
|
|
516
|
+
selector: str,
|
|
517
|
+
eids: List[int],
|
|
518
|
+
input_format: str = 'db',
|
|
519
|
+
output_format: str = 'cbor'
|
|
520
|
+
) -> %s:
|
|
521
|
+
'''%s
|
|
522
|
+
|
|
523
|
+
Args:
|
|
524
|
+
config: Database configuration as EDN string
|
|
525
|
+
selector: Pull pattern as EDN string
|
|
526
|
+
eids: List of entity IDs
|
|
527
|
+
input_format: Database input format ('db', 'history', 'since:ts', 'asof:ts')
|
|
528
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
%s
|
|
532
|
+
'''
|
|
533
|
+
eids_edn = '[' + ' '.join(str(e) for e in eids) + ']'
|
|
534
|
+
callback, get_result = make_callback(output_format)
|
|
535
|
+
get_dll().%s(
|
|
536
|
+
get_isolatethread(),
|
|
537
|
+
input_format.encode('utf8'),
|
|
538
|
+
config.encode('utf8'),
|
|
539
|
+
selector.encode('utf8'),
|
|
540
|
+
eids_edn.encode('utf8'),
|
|
541
|
+
output_format.encode('utf8'),
|
|
542
|
+
callback
|
|
543
|
+
)
|
|
544
|
+
return get_result()
|
|
545
|
+
"
|
|
546
|
+
py-name return-type doc return-type c-name)))
|
|
547
|
+
|
|
548
|
+
(defn generate-db-index
|
|
549
|
+
"Generate Python function for index operations."
|
|
550
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
551
|
+
(let [py-name (clj-name->python-name op-name)
|
|
552
|
+
c-name (get-c-name op-name config)]
|
|
553
|
+
(format "
|
|
554
|
+
def %s(
|
|
555
|
+
config: str,
|
|
556
|
+
index: str,
|
|
557
|
+
input_format: str = 'db',
|
|
558
|
+
output_format: str = 'cbor'
|
|
559
|
+
) -> %s:
|
|
560
|
+
'''%s
|
|
561
|
+
|
|
562
|
+
Args:
|
|
563
|
+
config: Database configuration as EDN string
|
|
564
|
+
index: Index keyword as EDN string (':eavt', ':aevt', ':avet', ':vaet')
|
|
565
|
+
input_format: Database input format ('db', 'history', 'since:ts', 'asof:ts')
|
|
566
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
567
|
+
|
|
568
|
+
Returns:
|
|
569
|
+
%s (list of [e a v tx added?] vectors)
|
|
570
|
+
'''
|
|
571
|
+
callback, get_result = make_callback(output_format)
|
|
572
|
+
get_dll().%s(
|
|
573
|
+
get_isolatethread(),
|
|
574
|
+
input_format.encode('utf8'),
|
|
575
|
+
config.encode('utf8'),
|
|
576
|
+
index.encode('utf8'),
|
|
577
|
+
output_format.encode('utf8'),
|
|
578
|
+
callback
|
|
579
|
+
)
|
|
580
|
+
return get_result()
|
|
581
|
+
"
|
|
582
|
+
py-name return-type doc return-type c-name)))
|
|
583
|
+
|
|
584
|
+
(defn generate-db-index-range
|
|
585
|
+
"Generate Python function for index-range operation."
|
|
586
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
587
|
+
(let [py-name (clj-name->python-name op-name)
|
|
588
|
+
c-name (get-c-name op-name config)]
|
|
589
|
+
(format "
|
|
590
|
+
def %s(
|
|
591
|
+
config: str,
|
|
592
|
+
attrid: str,
|
|
593
|
+
start: Any,
|
|
594
|
+
end: Any,
|
|
595
|
+
input_format: str = 'db',
|
|
596
|
+
output_format: str = 'cbor'
|
|
597
|
+
) -> %s:
|
|
598
|
+
'''%s
|
|
599
|
+
|
|
600
|
+
Args:
|
|
601
|
+
config: Database configuration as EDN string
|
|
602
|
+
attrid: Attribute keyword as EDN string (e.g., ':age')
|
|
603
|
+
start: Start value (will be converted to EDN)
|
|
604
|
+
end: End value (will be converted to EDN)
|
|
605
|
+
input_format: Database input format
|
|
606
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
%s
|
|
610
|
+
'''
|
|
611
|
+
# Convert Python values to EDN representation
|
|
612
|
+
def to_edn(v):
|
|
613
|
+
if isinstance(v, str):
|
|
614
|
+
return f'\"{v}\"'
|
|
615
|
+
elif isinstance(v, bool):
|
|
616
|
+
return 'true' if v else 'false'
|
|
617
|
+
elif v is None:
|
|
618
|
+
return 'nil'
|
|
619
|
+
else:
|
|
620
|
+
return str(v)
|
|
621
|
+
|
|
622
|
+
callback, get_result = make_callback(output_format)
|
|
623
|
+
get_dll().%s(
|
|
624
|
+
get_isolatethread(),
|
|
625
|
+
input_format.encode('utf8'),
|
|
626
|
+
config.encode('utf8'),
|
|
627
|
+
attrid.encode('utf8'),
|
|
628
|
+
to_edn(start).encode('utf8'),
|
|
629
|
+
to_edn(end).encode('utf8'),
|
|
630
|
+
output_format.encode('utf8'),
|
|
631
|
+
callback
|
|
632
|
+
)
|
|
633
|
+
return get_result()
|
|
634
|
+
"
|
|
635
|
+
py-name return-type doc return-type c-name)))
|
|
636
|
+
|
|
637
|
+
(defn generate-config-timestamp
|
|
638
|
+
"Generate Python function for config + timestamp operations."
|
|
639
|
+
[op-name {:keys [return-type doc] :as config}]
|
|
640
|
+
(let [py-name (clj-name->python-name op-name)
|
|
641
|
+
c-name (get-c-name op-name config)]
|
|
642
|
+
(format "
|
|
643
|
+
def %s(
|
|
644
|
+
config: str,
|
|
645
|
+
before_timestamp_ms: Optional[int] = None,
|
|
646
|
+
output_format: str = 'cbor'
|
|
647
|
+
) -> %s:
|
|
648
|
+
'''%s
|
|
649
|
+
|
|
650
|
+
Args:
|
|
651
|
+
config: Database configuration as EDN string
|
|
652
|
+
before_timestamp_ms: Unix timestamp in milliseconds (optional)
|
|
653
|
+
output_format: Output format ('json', 'edn', or 'cbor')
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
%s
|
|
657
|
+
'''
|
|
658
|
+
import time
|
|
659
|
+
if before_timestamp_ms is None:
|
|
660
|
+
before_timestamp_ms = int(time.time() * 1000)
|
|
661
|
+
|
|
662
|
+
callback, get_result = make_callback(output_format)
|
|
663
|
+
get_dll().%s(
|
|
664
|
+
get_isolatethread(),
|
|
665
|
+
config.encode('utf8'),
|
|
666
|
+
before_timestamp_ms,
|
|
667
|
+
output_format.encode('utf8'),
|
|
668
|
+
callback
|
|
669
|
+
)
|
|
670
|
+
return get_result()
|
|
671
|
+
"
|
|
672
|
+
py-name return-type doc return-type c-name)))
|
|
673
|
+
|
|
674
|
+
;; =============================================================================
|
|
675
|
+
;; Function Generation
|
|
676
|
+
;; =============================================================================
|
|
677
|
+
|
|
678
|
+
(defn generate-function
|
|
679
|
+
"Generate a single Python function using overlay model.
|
|
680
|
+
|
|
681
|
+
Args:
|
|
682
|
+
op-name - Operation symbol from api-specification
|
|
683
|
+
spec - Full specification from api-specification
|
|
684
|
+
overlay - Python-specific overlay configuration
|
|
685
|
+
|
|
686
|
+
Returns generated Python code string"
|
|
687
|
+
[op-name spec overlay]
|
|
688
|
+
(let [;; Derive or use return type
|
|
689
|
+
return-type (or (:return-type overlay)
|
|
690
|
+
(malli->python-type (:ret spec)))
|
|
691
|
+
;; Format documentation
|
|
692
|
+
docstring (format-python-docstring (:doc spec) (:examples spec))
|
|
693
|
+
;; Merge into config
|
|
694
|
+
config (assoc overlay
|
|
695
|
+
:return-type return-type
|
|
696
|
+
:doc docstring)
|
|
697
|
+
;; Select appropriate generator based on pattern
|
|
698
|
+
generator (case (:pattern overlay)
|
|
699
|
+
:config-query generate-config-query
|
|
700
|
+
:config-mutation generate-config-mutation
|
|
701
|
+
:query generate-query
|
|
702
|
+
:transact generate-transact
|
|
703
|
+
:db-only generate-db-only
|
|
704
|
+
:db-eid generate-db-eid
|
|
705
|
+
:db-selector-eid generate-db-selector-eid
|
|
706
|
+
:db-selector-eids generate-db-selector-eids
|
|
707
|
+
:db-index generate-db-index
|
|
708
|
+
:db-index-range generate-db-index-range
|
|
709
|
+
:config-timestamp generate-config-timestamp
|
|
710
|
+
(throw (ex-info (str "Unknown pattern: " (:pattern overlay))
|
|
711
|
+
{:op-name op-name :overlay overlay})))]
|
|
712
|
+
(generator op-name config)))
|
|
713
|
+
|
|
714
|
+
(defn generate-all-functions
|
|
715
|
+
"Generate all Python functions using overlay model.
|
|
716
|
+
|
|
717
|
+
Iterates over api-specification, checking each operation against:
|
|
718
|
+
- python-operations (overlay config)
|
|
719
|
+
- python-excluded-operations (exclusions)
|
|
720
|
+
|
|
721
|
+
Operations must be either implemented or excluded."
|
|
722
|
+
[]
|
|
723
|
+
(let [all-ops api-specification
|
|
724
|
+
implemented (keys python-operations)
|
|
725
|
+
excluded (keys python-excluded-operations)]
|
|
726
|
+
(->> all-ops
|
|
727
|
+
(keep (fn [[op-name spec]]
|
|
728
|
+
(cond
|
|
729
|
+
;; Explicitly excluded - skip
|
|
730
|
+
(contains? (set excluded) op-name)
|
|
731
|
+
nil
|
|
732
|
+
|
|
733
|
+
;; Has overlay config - generate it
|
|
734
|
+
(contains? python-operations op-name)
|
|
735
|
+
(let [overlay (get python-operations op-name)]
|
|
736
|
+
(generate-function op-name spec overlay))
|
|
737
|
+
|
|
738
|
+
;; Missing overlay - warn (but continue)
|
|
739
|
+
:else
|
|
740
|
+
(do
|
|
741
|
+
(println (str "⚠️ WARNING: No Python overlay for operation: " op-name))
|
|
742
|
+
nil))))
|
|
743
|
+
(str/join "\n"))))
|
|
744
|
+
|
|
745
|
+
;; =============================================================================
|
|
746
|
+
;; File Generation
|
|
747
|
+
;; =============================================================================
|
|
748
|
+
|
|
749
|
+
(def file-header
|
|
750
|
+
"\"\"\"Generated Datahike Python bindings.
|
|
751
|
+
|
|
752
|
+
This file is auto-generated from datahike.api.specification.
|
|
753
|
+
DO NOT EDIT MANUALLY - changes will be overwritten.
|
|
754
|
+
|
|
755
|
+
To regenerate: bb codegen-python
|
|
756
|
+
|
|
757
|
+
All functions use callback-based FFI to libdatahike with:
|
|
758
|
+
- Multiple output formats (json, edn, cbor)
|
|
759
|
+
- Proper exception handling
|
|
760
|
+
- Type annotations (PEP 484)
|
|
761
|
+
|
|
762
|
+
Temporal query variants via input_format parameter:
|
|
763
|
+
- 'db': Current database state
|
|
764
|
+
- 'history': Full history including retractions
|
|
765
|
+
- 'since:{timestamp_ms}': Database since timestamp
|
|
766
|
+
- 'asof:{timestamp_ms}': Database as-of timestamp
|
|
767
|
+
\"\"\"
|
|
768
|
+
from typing import Any, Dict, List, Tuple, Optional
|
|
769
|
+
from ._native import (
|
|
770
|
+
get_dll,
|
|
771
|
+
get_isolatethread,
|
|
772
|
+
make_callback,
|
|
773
|
+
prepare_query_inputs,
|
|
774
|
+
DatahikeException,
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
__all__ = [
|
|
778
|
+
'DatahikeException',
|
|
779
|
+
'database_exists',
|
|
780
|
+
'create_database',
|
|
781
|
+
'delete_database',
|
|
782
|
+
'q',
|
|
783
|
+
'transact',
|
|
784
|
+
'pull',
|
|
785
|
+
'pull_many',
|
|
786
|
+
'entity',
|
|
787
|
+
'datoms',
|
|
788
|
+
'seek_datoms',
|
|
789
|
+
'index_range',
|
|
790
|
+
'schema',
|
|
791
|
+
'reverse_schema',
|
|
792
|
+
'metrics',
|
|
793
|
+
'gc_storage',
|
|
794
|
+
]
|
|
795
|
+
")
|
|
796
|
+
|
|
797
|
+
(def file-footer
|
|
798
|
+
"
|
|
799
|
+
|
|
800
|
+
# Re-export exception
|
|
801
|
+
DatahikeException = DatahikeException
|
|
802
|
+
")
|
|
803
|
+
|
|
804
|
+
(defn generate-file-content
|
|
805
|
+
"Generate complete generated.py content."
|
|
806
|
+
[]
|
|
807
|
+
(str file-header
|
|
808
|
+
(generate-all-functions)
|
|
809
|
+
file-footer))
|
|
810
|
+
|
|
811
|
+
(defn write-python-bindings!
|
|
812
|
+
"Write generated Python bindings to output directory."
|
|
813
|
+
[output-dir]
|
|
814
|
+
(let [output-path (str output-dir "/generated.py")
|
|
815
|
+
content (generate-file-content)]
|
|
816
|
+
(io/make-parents output-path)
|
|
817
|
+
(spit output-path content)
|
|
818
|
+
(println "Generated:" output-path)))
|
|
819
|
+
|
|
820
|
+
;; =============================================================================
|
|
821
|
+
;; Main Entry Point
|
|
822
|
+
;; =============================================================================
|
|
823
|
+
|
|
824
|
+
(defn -main
|
|
825
|
+
"Generate Python bindings with coverage validation.
|
|
826
|
+
Usage: clojure -M -m datahike.codegen.python <output-dir>"
|
|
827
|
+
[& args]
|
|
828
|
+
(let [output-dir (or (first args) "pydatahike/src/datahike")]
|
|
829
|
+
(println "Generating Python bindings from specification...")
|
|
830
|
+
|
|
831
|
+
;; Validate coverage
|
|
832
|
+
(validation/validate-coverage "Python" python-operations python-excluded-operations)
|
|
833
|
+
(validation/validate-exclusion-reasons "Python" python-excluded-operations)
|
|
834
|
+
(validation/validate-overlay-completeness "Python" python-operations [:pattern])
|
|
835
|
+
|
|
836
|
+
;; Generate bindings
|
|
837
|
+
(write-python-bindings! output-dir)
|
|
838
|
+
(println "Done.")))
|