grafeo 0.2.4__cp312-cp312-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
grafeo/__init__.py ADDED
@@ -0,0 +1,32 @@
1
+ """
2
+ Grafeo - A high-performance, embeddable graph database.
3
+
4
+ This module provides Python bindings for the Grafeo graph database,
5
+ offering a Pythonic interface for graph operations and GQL queries.
6
+
7
+ Example:
8
+ >>> from grafeo import GrafeoDB
9
+ >>> db = GrafeoDB()
10
+ >>> node = db.create_node(["Person"], {"name": "Alice", "age": 30})
11
+ >>> result = db.execute("MATCH (n:Person) RETURN n")
12
+ >>> for row in result:
13
+ ... print(row)
14
+ """
15
+
16
+ from grafeo.grafeo import (
17
+ GrafeoDB,
18
+ Node,
19
+ Edge,
20
+ QueryResult,
21
+ Value,
22
+ __version__,
23
+ )
24
+
25
+ __all__ = [
26
+ "GrafeoDB",
27
+ "Node",
28
+ "Edge",
29
+ "QueryResult",
30
+ "Value",
31
+ "__version__",
32
+ ]
grafeo/cli.py ADDED
@@ -0,0 +1,320 @@
1
+ """
2
+ Grafeo CLI - Command-line interface for Grafeo graph databases.
3
+
4
+ This module provides a Python CLI wrapper for the Grafeo admin functionality.
5
+ """
6
+
7
+ import json
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ try:
13
+ import click
14
+ except ImportError:
15
+ print("Error: click is required for the CLI. Install with: uv add grafeo[cli]")
16
+ sys.exit(1)
17
+
18
+ from grafeo import GrafeoDB
19
+
20
+
21
+ def format_bytes(bytes_val: int) -> str:
22
+ """Format bytes as human-readable string."""
23
+ if bytes_val >= 1024**3:
24
+ return f"{bytes_val / (1024**3):.2f} GB"
25
+ elif bytes_val >= 1024**2:
26
+ return f"{bytes_val / (1024**2):.2f} MB"
27
+ elif bytes_val >= 1024:
28
+ return f"{bytes_val / 1024:.2f} KB"
29
+ else:
30
+ return f"{bytes_val} bytes"
31
+
32
+
33
+ def print_table(headers: list[str], rows: list[list[str]]) -> None:
34
+ """Print a simple table."""
35
+ if not rows:
36
+ return
37
+
38
+ # Calculate column widths
39
+ widths = [len(h) for h in headers]
40
+ for row in rows:
41
+ for i, cell in enumerate(row):
42
+ if i < len(widths):
43
+ widths[i] = max(widths[i], len(str(cell)))
44
+
45
+ # Print header
46
+ header_line = " | ".join(h.ljust(widths[i]) for i, h in enumerate(headers))
47
+ print(header_line)
48
+ print("-" * len(header_line))
49
+
50
+ # Print rows
51
+ for row in rows:
52
+ row_line = " | ".join(str(c).ljust(widths[i]) for i, c in enumerate(row))
53
+ print(row_line)
54
+
55
+
56
+ def print_key_value(items: list[tuple[str, str]], as_json: bool = False) -> None:
57
+ """Print key-value pairs."""
58
+ if as_json:
59
+ print(json.dumps(dict(items), indent=2))
60
+ else:
61
+ max_key = max(len(k) for k, _ in items)
62
+ for key, value in items:
63
+ print(f"{key.ljust(max_key)} : {value}")
64
+
65
+
66
+ @click.group()
67
+ @click.option("--format", "-f", type=click.Choice(["table", "json"]), default="table", help="Output format")
68
+ @click.option("--quiet", "-q", is_flag=True, help="Suppress progress messages")
69
+ @click.version_option()
70
+ @click.pass_context
71
+ def cli(ctx: click.Context, format: str, quiet: bool) -> None:
72
+ """Grafeo database administration tool."""
73
+ ctx.ensure_object(dict)
74
+ ctx.obj["format"] = format
75
+ ctx.obj["quiet"] = quiet
76
+
77
+
78
+ @cli.command()
79
+ @click.argument("path", type=click.Path(exists=True))
80
+ @click.pass_context
81
+ def info(ctx: click.Context, path: str) -> None:
82
+ """Display database information."""
83
+ db = GrafeoDB.open(path)
84
+ info_dict = db.info()
85
+
86
+ as_json = ctx.obj["format"] == "json"
87
+ if as_json:
88
+ print(json.dumps(info_dict, indent=2, default=str))
89
+ else:
90
+ items = [
91
+ ("Mode", str(info_dict.get("mode", "unknown"))),
92
+ ("Nodes", str(info_dict.get("node_count", 0))),
93
+ ("Edges", str(info_dict.get("edge_count", 0))),
94
+ ("Persistent", str(info_dict.get("is_persistent", False))),
95
+ ("Path", str(info_dict.get("path") or "(in-memory)")),
96
+ ("WAL Enabled", str(info_dict.get("wal_enabled", False))),
97
+ ("Version", str(info_dict.get("version", "unknown"))),
98
+ ]
99
+ print_key_value(items)
100
+
101
+
102
+ @cli.command()
103
+ @click.argument("path", type=click.Path(exists=True))
104
+ @click.pass_context
105
+ def stats(ctx: click.Context, path: str) -> None:
106
+ """Show detailed statistics."""
107
+ db = GrafeoDB.open(path)
108
+ stats_dict = db.detailed_stats()
109
+
110
+ as_json = ctx.obj["format"] == "json"
111
+ if as_json:
112
+ print(json.dumps(stats_dict, indent=2, default=str))
113
+ else:
114
+ memory = stats_dict.get("memory_bytes", 0)
115
+ disk = stats_dict.get("disk_bytes")
116
+ items = [
117
+ ("Nodes", str(stats_dict.get("node_count", 0))),
118
+ ("Edges", str(stats_dict.get("edge_count", 0))),
119
+ ("Labels", str(stats_dict.get("label_count", 0))),
120
+ ("Edge Types", str(stats_dict.get("edge_type_count", 0))),
121
+ ("Property Keys", str(stats_dict.get("property_key_count", 0))),
122
+ ("Indexes", str(stats_dict.get("index_count", 0))),
123
+ ("Memory Usage", format_bytes(memory)),
124
+ ("Disk Usage", format_bytes(disk) if disk else "N/A"),
125
+ ]
126
+ print_key_value(items)
127
+
128
+
129
+ @cli.command()
130
+ @click.argument("path", type=click.Path(exists=True))
131
+ @click.pass_context
132
+ def schema(ctx: click.Context, path: str) -> None:
133
+ """Display schema information."""
134
+ db = GrafeoDB.open(path)
135
+ schema_dict = db.schema()
136
+
137
+ as_json = ctx.obj["format"] == "json"
138
+ if as_json:
139
+ print(json.dumps(schema_dict, indent=2, default=str))
140
+ else:
141
+ mode = schema_dict.get("mode", "lpg")
142
+ print(f"Mode: {mode.upper()}\n")
143
+
144
+ if mode == "lpg":
145
+ labels = schema_dict.get("labels", [])
146
+ if labels:
147
+ print("Labels:")
148
+ print_table(["Name", "Count"], [[l["name"], str(l["count"])] for l in labels])
149
+ print()
150
+
151
+ edge_types = schema_dict.get("edge_types", [])
152
+ if edge_types:
153
+ print("Edge Types:")
154
+ print_table(["Name", "Count"], [[e["name"], str(e["count"])] for e in edge_types])
155
+ print()
156
+
157
+ prop_keys = schema_dict.get("property_keys", [])
158
+ if prop_keys:
159
+ print("Property Keys:")
160
+ for key in prop_keys:
161
+ print(f" - {key}")
162
+ else:
163
+ predicates = schema_dict.get("predicates", [])
164
+ if predicates:
165
+ print("Predicates:")
166
+ print_table(["IRI", "Count"], [[p["iri"], str(p["count"])] for p in predicates])
167
+ print()
168
+
169
+ graphs = schema_dict.get("named_graphs", [])
170
+ if graphs:
171
+ print("Named Graphs:")
172
+ for g in graphs:
173
+ print(f" - {g}")
174
+
175
+
176
+ @cli.command()
177
+ @click.argument("path", type=click.Path(exists=True))
178
+ @click.pass_context
179
+ def validate(ctx: click.Context, path: str) -> None:
180
+ """Validate database integrity."""
181
+ db = GrafeoDB.open(path)
182
+ result = db.validate()
183
+
184
+ as_json = ctx.obj["format"] == "json"
185
+ if as_json:
186
+ print(json.dumps(result, indent=2, default=str))
187
+ else:
188
+ errors = result.get("errors", [])
189
+ warnings = result.get("warnings", [])
190
+
191
+ if not errors:
192
+ click.secho("Database is valid", fg="green")
193
+ else:
194
+ click.secho("Database has errors", fg="red")
195
+
196
+ print(f"\nErrors: {len(errors)}, Warnings: {len(warnings)}")
197
+
198
+ if errors:
199
+ print("\nErrors:")
200
+ print_table(
201
+ ["Code", "Message", "Context"],
202
+ [[e["code"], e["message"], e.get("context", "-")] for e in errors],
203
+ )
204
+
205
+ if warnings:
206
+ print("\nWarnings:")
207
+ print_table(
208
+ ["Code", "Message", "Context"],
209
+ [[w["code"], w["message"], w.get("context", "-")] for w in warnings],
210
+ )
211
+
212
+ if errors:
213
+ sys.exit(1)
214
+
215
+
216
+ @cli.group()
217
+ def wal() -> None:
218
+ """Manage Write-Ahead Log."""
219
+ pass
220
+
221
+
222
+ @wal.command("status")
223
+ @click.argument("path", type=click.Path(exists=True))
224
+ @click.pass_context
225
+ def wal_status(ctx: click.Context, path: str) -> None:
226
+ """Show WAL status."""
227
+ db = GrafeoDB.open(path)
228
+ status = db.wal_status()
229
+
230
+ as_json = ctx.obj["format"] == "json"
231
+ if as_json:
232
+ print(json.dumps(status, indent=2, default=str))
233
+ else:
234
+ items = [
235
+ ("Enabled", str(status.get("enabled", False))),
236
+ ("Path", str(status.get("path") or "N/A")),
237
+ ("Size", format_bytes(status.get("size_bytes", 0))),
238
+ ("Records", str(status.get("record_count", 0))),
239
+ ("Last Checkpoint", str(status.get("last_checkpoint") or "Never")),
240
+ ("Current Epoch", str(status.get("current_epoch", 0))),
241
+ ]
242
+ print_key_value(items)
243
+
244
+
245
+ @wal.command("checkpoint")
246
+ @click.argument("path", type=click.Path(exists=True))
247
+ @click.pass_context
248
+ def wal_checkpoint(ctx: click.Context, path: str) -> None:
249
+ """Force a WAL checkpoint."""
250
+ quiet = ctx.obj["quiet"]
251
+ if not quiet:
252
+ click.echo("Forcing WAL checkpoint...")
253
+
254
+ db = GrafeoDB.open(path)
255
+ db.wal_checkpoint()
256
+
257
+ if not quiet:
258
+ click.secho("WAL checkpoint completed", fg="green")
259
+
260
+
261
+ @cli.group()
262
+ def backup() -> None:
263
+ """Manage backups."""
264
+ pass
265
+
266
+
267
+ @backup.command("create")
268
+ @click.argument("path", type=click.Path(exists=True))
269
+ @click.option("--output", "-o", required=True, type=click.Path(), help="Output backup path")
270
+ @click.pass_context
271
+ def backup_create(ctx: click.Context, path: str, output: str) -> None:
272
+ """Create a database backup."""
273
+ quiet = ctx.obj["quiet"]
274
+ if not quiet:
275
+ click.echo(f"Creating backup of {path}...")
276
+
277
+ db = GrafeoDB.open(path)
278
+ db.save(output)
279
+
280
+ if not quiet:
281
+ click.secho(f"Backup created at {output}", fg="green")
282
+
283
+
284
+ @backup.command("restore")
285
+ @click.argument("backup_path", type=click.Path(exists=True))
286
+ @click.argument("target_path", type=click.Path())
287
+ @click.option("--force", is_flag=True, help="Overwrite existing target")
288
+ @click.pass_context
289
+ def backup_restore(ctx: click.Context, backup_path: str, target_path: str, force: bool) -> None:
290
+ """Restore from a backup."""
291
+ quiet = ctx.obj["quiet"]
292
+ target = Path(target_path)
293
+
294
+ if target.exists() and not force:
295
+ click.echo(f"Error: Target {target_path} already exists. Use --force to overwrite.", err=True)
296
+ sys.exit(1)
297
+
298
+ if target.exists() and force:
299
+ if not quiet:
300
+ click.echo(f"Removing existing database at {target_path}...")
301
+ import shutil
302
+ shutil.rmtree(target_path)
303
+
304
+ if not quiet:
305
+ click.echo(f"Restoring from {backup_path}...")
306
+
307
+ db = GrafeoDB.open(backup_path)
308
+ db.save(target_path)
309
+
310
+ if not quiet:
311
+ click.secho(f"Database restored to {target_path}", fg="green")
312
+
313
+
314
+ def main() -> None:
315
+ """Entry point for the CLI."""
316
+ cli(obj={})
317
+
318
+
319
+ if __name__ == "__main__":
320
+ main()
Binary file
grafeo/py.typed ADDED
File without changes
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: grafeo
3
+ Version: 0.2.4
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: Intended Audience :: Science/Research
7
+ Classifier: License :: OSI Approved :: Apache Software License
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Programming Language :: Rust
14
+ Classifier: Topic :: Database
15
+ Classifier: Topic :: Database :: Database Engines/Servers
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Classifier: Typing :: Typed
18
+ Requires-Dist: solvor>=0.6.1
19
+ Requires-Dist: click>=8.3.1 ; extra == 'cli'
20
+ Requires-Dist: pytest>=9.0.2 ; extra == 'dev'
21
+ Requires-Dist: pytest-asyncio>=1.3.0 ; extra == 'dev'
22
+ Requires-Dist: ty>=0.0.14 ; extra == 'dev'
23
+ Requires-Dist: ruff>=0.14.14 ; extra == 'dev'
24
+ Provides-Extra: cli
25
+ Provides-Extra: dev
26
+ Summary: A high-performance, embeddable graph database with Python bindings
27
+ Keywords: graph,database,gql,knowledge-graph,embedded
28
+ Author: S.T. Grond
29
+ License: Apache-2.0
30
+ Requires-Python: >=3.12
31
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
32
+ Project-URL: Documentation, https://grafeo.dev/user-guide/
33
+ Project-URL: Homepage, https://grafeo.dev
34
+ Project-URL: Issues, https://github.com/GrafeoDB/grafeo/issues
35
+ Project-URL: Repository, https://github.com/GrafeoDB/grafeo
36
+
37
+ # grafeo
38
+
39
+ Python bindings for Grafeo, a pure-Rust, high-performance, embeddable graph database.
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ uv add grafeo
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ```python
50
+ import grafeo
51
+
52
+ # Create an in-memory database
53
+ db = grafeo.GrafeoDB()
54
+
55
+ # Create nodes using GQL
56
+ db.execute("INSERT (:Person {name: 'Alice', age: 30})")
57
+ db.execute("INSERT (:Person {name: 'Bob', age: 25})")
58
+
59
+ # Query the graph
60
+ result = db.execute("MATCH (p:Person) RETURN p.name, p.age")
61
+ for row in result:
62
+ print(row)
63
+ ```
64
+
65
+ ## License
66
+
67
+ Apache-2.0
68
+
@@ -0,0 +1,8 @@
1
+ grafeo\__init__.py,sha256=HjIOQVzybwDT4MRuQB-NuJ3Pwwn1LOvLJCaIl3V6iVg,698
2
+ grafeo\cli.py,sha256=_x3wgb8AuQl8IJ89HGApMbK7eZ4dvstf1KWt_4BIW4I,10256
3
+ grafeo\grafeo.cp312-win32.pyd,sha256=38kRJZBbWaemwgzIjoHktSvuPZGtIk3_OSSln0DdeiU,4622336
4
+ grafeo\py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ grafeo-0.2.4.dist-info\METADATA,sha256=aIUEtTWKAVhX0E1DkyiTSsugMHo7EgN_e01WvUCvQNM,2056
6
+ grafeo-0.2.4.dist-info\WHEEL,sha256=akdtKPhgZFKWiFB2PSXRQPXzLs0y-XAkg8eqWX78MDg,93
7
+ grafeo-0.2.4.dist-info\entry_points.txt,sha256=kNAJ9wDtkgCY-nfE5IXQaf-bJi1kKRplXKdZr6xavDs,41
8
+ grafeo-0.2.4.dist-info\RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.11.5)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-win32
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ grafeo=grafeo.cli:main