zyxdb 0.1.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.
zyxdb-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.1
2
+ Name: zyxdb
3
+ Version: 0.1.0
4
+ Summary: Python bindings for ZYX graph database engine
5
+ License: Apache-2.0
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: License :: OSI Approved :: Apache Software License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: C++
16
+ Classifier: Topic :: Database
17
+ Classifier: Topic :: Database :: Database Engines/Servers
18
+ Project-URL: Homepage, https://github.com/nexepic/zyx
19
+ Project-URL: Repository, https://github.com/nexepic/zyx
20
+ Requires-Python: >=3.9
21
+ Provides-Extra: test
22
+ Requires-Dist: pytest>=7.0; extra == "test"
23
+ Description-Content-Type: text/markdown
24
+
25
+ # zyxdb
26
+
27
+ Python bindings for [ZYX](https://github.com/nicklauslititz/zyx), a high-performance embeddable graph database engine with Cypher query support.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install zyxdb
33
+ ```
34
+
35
+ ### Build from source
36
+
37
+ ```bash
38
+ cd bindings/python
39
+ pip install -e ".[test]"
40
+ ```
41
+
42
+ ## Quick Start
43
+
44
+ ```python
45
+ import zyxdb
46
+
47
+ # Open a database (creates if not exists)
48
+ db = zyxdb.Database("/tmp/mydb")
49
+
50
+ # Create nodes with Cypher
51
+ db.execute("CREATE (n:Person {name: $name, age: $age})", name="Alice", age=30)
52
+
53
+ # Query with iteration
54
+ for row in db.execute("MATCH (n:Person) RETURN n.name AS name, n.age AS age"):
55
+ print(row["name"], row["age"])
56
+
57
+ # Batch insert
58
+ db.create_nodes("Person", [
59
+ {"name": "Bob", "age": 25},
60
+ {"name": "Carol", "age": 35},
61
+ ])
62
+
63
+ # Transactions with context manager
64
+ with db.begin_transaction() as tx:
65
+ tx.execute("CREATE (n:Movie {title: 'Matrix'})")
66
+ tx.execute(
67
+ "MATCH (a:Person {name: 'Alice'}), (m:Movie {title: 'Matrix'}) "
68
+ "CREATE (a)-[:WATCHED]->(m)"
69
+ )
70
+ tx.commit()
71
+
72
+ # Read-only transaction
73
+ with db.begin_read_only_transaction() as tx:
74
+ for row in tx.execute("MATCH (n) RETURN count(n) AS cnt"):
75
+ print(row["cnt"])
76
+
77
+ # Direct node/edge creation by ID (high performance)
78
+ id1 = db.create_node_ret_id("Person", {"name": "Dave"})
79
+ id2 = db.create_node_ret_id("Person", {"name": "Eve"})
80
+ db.create_edge_by_id(id1, id2, "KNOWS", {"since": 2024})
81
+
82
+ # Shortest path
83
+ path = db.get_shortest_path(id1, id2)
84
+
85
+ db.close()
86
+ ```
87
+
88
+ ## Context Manager
89
+
90
+ ```python
91
+ with zyxdb.Database("/tmp/mydb") as db:
92
+ db.execute("CREATE (n:Test {x: 1})")
93
+ # Auto-closes on exit
94
+ ```
95
+
96
+ ## Supported Value Types
97
+
98
+ | Python | ZYX |
99
+ |--------|-----|
100
+ | `None` | null |
101
+ | `bool` | bool |
102
+ | `int` | int64 |
103
+ | `float` | double |
104
+ | `str` | string |
105
+ | `list[float]` | vector (embeddings) |
106
+ | `list[str]` | string list |
107
+ | `list` (mixed) | heterogeneous list |
108
+ | `dict` | map |
109
+
110
+ ## API Reference
111
+
112
+ ### `Database(path, *, open=True)`
113
+
114
+ - `execute(cypher, **params)` — Execute Cypher query with keyword parameters
115
+ - `begin_transaction()` — Start a read-write transaction
116
+ - `begin_read_only_transaction()` — Start a read-only transaction
117
+ - `create_node(label, props)` — Create a node (label can be `str` or `list[str]`)
118
+ - `create_node_ret_id(label, props)` — Create a node, return its ID
119
+ - `create_nodes(label, props_list)` — Batch create nodes
120
+ - `create_edge_by_id(src_id, dst_id, type, props)` — Create edge between known IDs
121
+ - `get_shortest_path(start_id, end_id, max_depth=15)` — Find shortest path
122
+ - `save()` — Flush to disk
123
+ - `close()` — Close the database
124
+
125
+ ### `Transaction`
126
+
127
+ - `execute(cypher, **params)` — Execute within transaction
128
+ - `commit()` — Commit changes
129
+ - `rollback()` — Roll back changes
130
+ - `is_active` — Whether transaction is still active
131
+ - `is_read_only` — Whether read-only
132
+
133
+ ### `Result`
134
+
135
+ - Iterable: `for row in result` yields `dict[str, Any]`
136
+ - `column_names` — List of column names
137
+ - `duration` — Query execution time (ms)
138
+ - `is_success` — Whether query succeeded
139
+ - `error` — Error message or `None`
140
+
141
+ ## License
142
+
143
+ Apache License 2.0
zyxdb-0.1.0/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # zyxdb
2
+
3
+ Python bindings for [ZYX](https://github.com/nicklauslititz/zyx), a high-performance embeddable graph database engine with Cypher query support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install zyxdb
9
+ ```
10
+
11
+ ### Build from source
12
+
13
+ ```bash
14
+ cd bindings/python
15
+ pip install -e ".[test]"
16
+ ```
17
+
18
+ ## Quick Start
19
+
20
+ ```python
21
+ import zyxdb
22
+
23
+ # Open a database (creates if not exists)
24
+ db = zyxdb.Database("/tmp/mydb")
25
+
26
+ # Create nodes with Cypher
27
+ db.execute("CREATE (n:Person {name: $name, age: $age})", name="Alice", age=30)
28
+
29
+ # Query with iteration
30
+ for row in db.execute("MATCH (n:Person) RETURN n.name AS name, n.age AS age"):
31
+ print(row["name"], row["age"])
32
+
33
+ # Batch insert
34
+ db.create_nodes("Person", [
35
+ {"name": "Bob", "age": 25},
36
+ {"name": "Carol", "age": 35},
37
+ ])
38
+
39
+ # Transactions with context manager
40
+ with db.begin_transaction() as tx:
41
+ tx.execute("CREATE (n:Movie {title: 'Matrix'})")
42
+ tx.execute(
43
+ "MATCH (a:Person {name: 'Alice'}), (m:Movie {title: 'Matrix'}) "
44
+ "CREATE (a)-[:WATCHED]->(m)"
45
+ )
46
+ tx.commit()
47
+
48
+ # Read-only transaction
49
+ with db.begin_read_only_transaction() as tx:
50
+ for row in tx.execute("MATCH (n) RETURN count(n) AS cnt"):
51
+ print(row["cnt"])
52
+
53
+ # Direct node/edge creation by ID (high performance)
54
+ id1 = db.create_node_ret_id("Person", {"name": "Dave"})
55
+ id2 = db.create_node_ret_id("Person", {"name": "Eve"})
56
+ db.create_edge_by_id(id1, id2, "KNOWS", {"since": 2024})
57
+
58
+ # Shortest path
59
+ path = db.get_shortest_path(id1, id2)
60
+
61
+ db.close()
62
+ ```
63
+
64
+ ## Context Manager
65
+
66
+ ```python
67
+ with zyxdb.Database("/tmp/mydb") as db:
68
+ db.execute("CREATE (n:Test {x: 1})")
69
+ # Auto-closes on exit
70
+ ```
71
+
72
+ ## Supported Value Types
73
+
74
+ | Python | ZYX |
75
+ |--------|-----|
76
+ | `None` | null |
77
+ | `bool` | bool |
78
+ | `int` | int64 |
79
+ | `float` | double |
80
+ | `str` | string |
81
+ | `list[float]` | vector (embeddings) |
82
+ | `list[str]` | string list |
83
+ | `list` (mixed) | heterogeneous list |
84
+ | `dict` | map |
85
+
86
+ ## API Reference
87
+
88
+ ### `Database(path, *, open=True)`
89
+
90
+ - `execute(cypher, **params)` — Execute Cypher query with keyword parameters
91
+ - `begin_transaction()` — Start a read-write transaction
92
+ - `begin_read_only_transaction()` — Start a read-only transaction
93
+ - `create_node(label, props)` — Create a node (label can be `str` or `list[str]`)
94
+ - `create_node_ret_id(label, props)` — Create a node, return its ID
95
+ - `create_nodes(label, props_list)` — Batch create nodes
96
+ - `create_edge_by_id(src_id, dst_id, type, props)` — Create edge between known IDs
97
+ - `get_shortest_path(start_id, end_id, max_depth=15)` — Find shortest path
98
+ - `save()` — Flush to disk
99
+ - `close()` — Close the database
100
+
101
+ ### `Transaction`
102
+
103
+ - `execute(cypher, **params)` — Execute within transaction
104
+ - `commit()` — Commit changes
105
+ - `rollback()` — Roll back changes
106
+ - `is_active` — Whether transaction is still active
107
+ - `is_read_only` — Whether read-only
108
+
109
+ ### `Result`
110
+
111
+ - Iterable: `for row in result` yields `dict[str, Any]`
112
+ - `column_names` — List of column names
113
+ - `duration` — Query execution time (ms)
114
+ - `is_success` — Whether query succeeded
115
+ - `error` — Error message or `None`
116
+
117
+ ## License
118
+
119
+ Apache License 2.0
@@ -0,0 +1,222 @@
1
+ project('zyxdb', 'cpp',
2
+ version : run_command(
3
+ 'python3', '-c',
4
+ 'import re, pathlib; print(re.search(r"version\\s*:\\s*\'([^\']+)\'", pathlib.Path("' + meson.current_source_dir() / '..' / '..' / 'meson.build' + '").read_text()).group(1))',
5
+ check : true).stdout().strip(),
6
+ default_options : [
7
+ 'cpp_std=c++20',
8
+ 'warning_level=3',
9
+ 'werror=false',
10
+ 'buildtype=release',
11
+ 'default_library=static',
12
+ ])
13
+
14
+ # Resolve the root of the main ZYX project (two levels up from bindings/python)
15
+ zyx_root = meson.current_source_dir() / '..' / '..'
16
+
17
+ cxx = meson.get_compiler('cpp')
18
+
19
+ # Windows macro conflict guards
20
+ if host_machine.system() == 'windows'
21
+ add_project_arguments('-DNOMINMAX', '-DNOGDI', language : 'cpp')
22
+ endif
23
+
24
+ # ==============================================================================
25
+ # Dependencies (same as root meson.build)
26
+ # ==============================================================================
27
+ zlib_dep = dependency('zlib')
28
+ boost_dep = dependency('boost', modules : ['filesystem', 'system'])
29
+ cli11_dep = dependency('CLI11')
30
+
31
+ antlr4_dep = dependency('antlr4-cppruntime', required : false)
32
+ if not antlr4_dep.found()
33
+ antlr4_dep = dependency('antlr4-runtime', method : 'cmake',
34
+ modules : ['antlr4_static'], required : true)
35
+ endif
36
+
37
+ # ==============================================================================
38
+ # ANTLR4 generated parser sources + implementation
39
+ # ==============================================================================
40
+ parser_dir = zyx_root / 'src' / 'query' / 'parser' / 'cypher' / 'generated'
41
+ parser_impl_dir = zyx_root / 'src' / 'query' / 'parser' / 'cypher'
42
+
43
+ _py = import('python').find_installation()
44
+
45
+ # Generated parser files
46
+ _find_parser = run_command(_py, '-c', '''
47
+ import os, sys
48
+ d = sys.argv[1]
49
+ source_dir = sys.argv[2]
50
+ for f in sorted(os.listdir(d)):
51
+ if f.endswith(".cpp"):
52
+ print(os.path.relpath(os.path.join(d, f), source_dir))
53
+ ''', parser_dir, meson.current_source_dir(), check : true)
54
+ _parser_files = _find_parser.stdout().strip().split('\n')
55
+ parser_src = []
56
+ foreach f : _parser_files
57
+ if f != ''
58
+ parser_src += files(f)
59
+ endif
60
+ endforeach
61
+
62
+ # Parser implementation files (CypherParserImpl, helpers, clauses, etc.)
63
+ _find_parser_impl = run_command(_py, '-c', '''
64
+ import os, sys
65
+ base_dir = sys.argv[1]
66
+ source_dir = sys.argv[2]
67
+ seen = set()
68
+ for line in sys.argv[3].splitlines():
69
+ if line.strip():
70
+ seen.add(os.path.normpath(line.strip()))
71
+ for root, dirs, files_list in os.walk(base_dir):
72
+ for f in sorted(files_list):
73
+ if f.endswith(".cpp"):
74
+ rel = os.path.relpath(os.path.join(root, f), source_dir)
75
+ if os.path.normpath(rel) not in seen:
76
+ print(rel)
77
+ ''', parser_impl_dir, meson.current_source_dir(), '\n'.join(_parser_files), check : true)
78
+ _parser_impl_files = _find_parser_impl.stdout().strip().split('\n')
79
+ foreach f : _parser_impl_files
80
+ if f != ''
81
+ parser_src += files(f)
82
+ endif
83
+ endforeach
84
+
85
+ cypher_inc = include_directories(
86
+ '../../src/query/parser/cypher/generated',
87
+ '../../src/query/parser/cypher',
88
+ '../../src/query/parser/cypher/helpers/internal',
89
+ '../../src/query/parser/cypher/helpers/public',
90
+ )
91
+
92
+ cypher_dep = declare_dependency(
93
+ sources : parser_src,
94
+ include_directories : cypher_inc,
95
+ )
96
+
97
+ # ==============================================================================
98
+ # inputxx library (required by core/cli)
99
+ # ==============================================================================
100
+ inputxx_src_dir = zyx_root / 'lib' / 'inputxx' / 'src'
101
+ _find_inputxx = run_command(_py, '-c', '''
102
+ import os, sys
103
+ base_dir = sys.argv[1]
104
+ for root, dirs, files in os.walk(base_dir):
105
+ for f in sorted(files):
106
+ if f.endswith(".cpp"):
107
+ print(os.path.relpath(os.path.join(root, f), base_dir))
108
+ ''', inputxx_src_dir, check : true)
109
+ inputxx_src = []
110
+ foreach f : _find_inputxx.stdout().strip().split('\n')
111
+ if f != ''
112
+ inputxx_src += files(zyx_root / 'lib' / 'inputxx' / 'src' / f)
113
+ endif
114
+ endforeach
115
+ inputxx_inc = include_directories('../../lib/inputxx/include')
116
+ inputxx_lib = static_library('inputxx',
117
+ sources : inputxx_src,
118
+ include_directories : inputxx_inc,
119
+ install : false,
120
+ )
121
+ inputxx_dep = declare_dependency(
122
+ include_directories : inputxx_inc,
123
+ link_with : inputxx_lib,
124
+ )
125
+
126
+ # ==============================================================================
127
+ # Core library sources (same discovery as src/meson.build)
128
+ # ==============================================================================
129
+ src_dir = zyx_root / 'src'
130
+ _find_src = run_command(_py, '-c', '''
131
+ import os, sys
132
+ base_dir = sys.argv[1]
133
+ source_dir = sys.argv[2]
134
+ exclude = ["query/parser", "cli"]
135
+ for root, dirs, files_list in os.walk(base_dir):
136
+ rel_root = os.path.relpath(root, base_dir)
137
+ skip = False
138
+ for ex in exclude:
139
+ if rel_root == ex or rel_root.startswith(ex + os.sep):
140
+ skip = True
141
+ break
142
+ if skip:
143
+ continue
144
+ for f in sorted(files_list):
145
+ if f.endswith(".cpp"):
146
+ print(os.path.relpath(os.path.join(root, f), source_dir))
147
+ ''', src_dir, meson.current_source_dir(), check : true)
148
+ _src_files = _find_src.stdout().strip().split('\n')
149
+ db_src = []
150
+ foreach f : _src_files
151
+ if f != ''
152
+ db_src += files(f)
153
+ endif
154
+ endforeach
155
+
156
+ inc_dirs = include_directories(
157
+ '../../include',
158
+ '../../src',
159
+ '../..',
160
+ '../../src/query/parser/cypher',
161
+ )
162
+
163
+ # Generate ProjectConfig.hpp from template
164
+ project_config = configuration_data()
165
+ project_config.set('PROJECT_NAME', 'zyx')
166
+ project_config.set('PROJECT_DISPLAY', 'ZYX')
167
+ project_config.set('PROJECT_VERSION', meson.project_version())
168
+ configure_file(
169
+ input: zyx_root / 'include' / 'graph' / 'core' / 'ProjectConfig.hpp.in',
170
+ output: 'ProjectConfig.hpp',
171
+ configuration: project_config,
172
+ install: false,
173
+ )
174
+
175
+ # Python's Token.h defines macros (SEMI, COMMA, etc.) that conflict with
176
+ # ANTLR4 parser method names. Force-include an undef header to neutralize them.
177
+ _undef_header = meson.current_source_dir() / 'undef_python_tokens.h'
178
+ if cxx.get_argument_syntax() == 'msvc'
179
+ _force_include = ['/FI' + _undef_header]
180
+ else
181
+ _force_include = ['-include', _undef_header]
182
+ endif
183
+
184
+ db_lib = static_library('zyx_core',
185
+ sources : db_src,
186
+ include_directories : inc_dirs,
187
+ dependencies : [zlib_dep, boost_dep, cli11_dep, antlr4_dep, cypher_dep, inputxx_dep],
188
+ cpp_args : ['-DCOVERAGE_SKIP_ZLIB_ERRORS'] + _force_include,
189
+ install : false,
190
+ )
191
+
192
+ db_dep = declare_dependency(
193
+ include_directories : [inc_dirs, cypher_inc],
194
+ link_with : db_lib,
195
+ dependencies : [zlib_dep, boost_dep, cli11_dep, antlr4_dep, inputxx_dep],
196
+ )
197
+
198
+ # ==============================================================================
199
+ # Python extension module
200
+ # ==============================================================================
201
+ py = import('python').find_installation(pure: false)
202
+
203
+ pybind11_dep = dependency('pybind11', required: true)
204
+
205
+ zyxdb_core = py.extension_module(
206
+ '_core',
207
+ sources: ['src/zyxdb/_core.cpp'],
208
+ dependencies: [pybind11_dep, db_dep],
209
+ include_directories: inc_dirs,
210
+ install: true,
211
+ subdir: 'zyxdb',
212
+ )
213
+
214
+ py.install_sources(
215
+ 'src/zyxdb/__init__.py',
216
+ 'src/zyxdb/database.py',
217
+ 'src/zyxdb/result.py',
218
+ 'src/zyxdb/transaction.py',
219
+ 'src/zyxdb/types.py',
220
+ 'src/zyxdb/py.typed',
221
+ subdir: 'zyxdb',
222
+ )
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["meson-python>=0.16.0", "pybind11>=2.13"]
3
+ build-backend = "mesonpy"
4
+
5
+ [project]
6
+ name = "zyxdb"
7
+ description = "Python bindings for ZYX graph database engine"
8
+ dynamic = ["version"]
9
+ readme = "README.md"
10
+ license = {text = "Apache-2.0"}
11
+ requires-python = ">=3.9"
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: Apache Software License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.9",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: C++",
23
+ "Topic :: Database",
24
+ "Topic :: Database :: Database Engines/Servers",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ test = ["pytest>=7.0"]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/nexepic/zyx"
32
+ Repository = "https://github.com/nexepic/zyx"
@@ -0,0 +1,23 @@
1
+ """zyxdb — Python bindings for ZYX graph database engine."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ from zyxdb._core import DatabaseError
6
+ from zyxdb.database import Database
7
+ from zyxdb.result import Record, Result
8
+ from zyxdb.transaction import Transaction
9
+ from zyxdb.types import Edge, Node
10
+
11
+ try:
12
+ __version__ = version("zyxdb")
13
+ except PackageNotFoundError:
14
+ __version__ = "0.0.0-dev"
15
+ __all__ = [
16
+ "Database",
17
+ "DatabaseError",
18
+ "Edge",
19
+ "Node",
20
+ "Record",
21
+ "Result",
22
+ "Transaction",
23
+ ]