grafeo 0.1.1__cp313-cp313t-musllinux_1_2_aarch64.whl → 0.2.4__cp313-cp313t-musllinux_1_2_aarch64.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.

Potentially problematic release.


This version of grafeo might be problematic. Click here for more details.

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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grafeo
3
- Version: 0.1.1
3
+ Version: 0.2.4
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: Intended Audience :: Science/Research
@@ -15,36 +15,51 @@ Classifier: Topic :: Database
15
15
  Classifier: Topic :: Database :: Database Engines/Servers
16
16
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
17
  Classifier: Typing :: Typed
18
+ Requires-Dist: solvor>=0.6.1
19
+ Requires-Dist: click>=8.3.1 ; extra == 'cli'
18
20
  Requires-Dist: pytest>=9.0.2 ; extra == 'dev'
19
21
  Requires-Dist: pytest-asyncio>=1.3.0 ; extra == 'dev'
20
- Requires-Dist: ty>=0.13.0 ; extra == 'dev'
21
- Requires-Dist: ruff>=0.1 ; extra == 'dev'
22
+ Requires-Dist: ty>=0.0.14 ; extra == 'dev'
23
+ Requires-Dist: ruff>=0.14.14 ; extra == 'dev'
24
+ Provides-Extra: cli
22
25
  Provides-Extra: dev
23
26
  Summary: A high-performance, embeddable graph database with Python bindings
24
27
  Keywords: graph,database,gql,knowledge-graph,embedded
25
- Author: Grafeo Contributors
28
+ Author: S.T. Grond
26
29
  License: Apache-2.0
27
30
  Requires-Python: >=3.12
28
31
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
29
- Project-URL: Documentation, https://grafeo.tech
30
- Project-URL: Homepage, https://grafeo.tech
31
- Project-URL: Issues, https://github.com/StevenBtw/grafeo/issues
32
- Project-URL: Repository, https://github.com/StevenBtw/grafeo
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
33
36
 
34
- # pygrafeo
37
+ # grafeo
35
38
 
36
39
  Python bindings for Grafeo, a pure-Rust, high-performance, embeddable graph database.
37
40
 
38
41
  ## Installation
39
42
 
40
43
  ```bash
41
- pip install pygrafeo
44
+ uv add grafeo
42
45
  ```
43
46
 
44
47
  ## Usage
45
48
 
46
49
  ```python
47
- import pygrafeo
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)
48
63
  ```
49
64
 
50
65
  ## License
@@ -0,0 +1,9 @@
1
+ grafeo/__init__.py,sha256=k_RJXamdSIXvR1VOVd5higAea76QziQpMIKmmpp4pBc,666
2
+ grafeo/cli.py,sha256=7pGVzq1eMpltb9ItLGDr5h_8OlYgtIO3WdO04nPaTnk,9936
3
+ grafeo/grafeo.cpython-313t-aarch64-linux-musl.so,sha256=yyshQqvxApLnFzpdRbPwtpJdZpJWDoFuFX4VgFWt3_g,5705345
4
+ grafeo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ grafeo-0.2.4.dist-info/METADATA,sha256=jV6s6_98JWP4nE0KLUNTyb1bSjfj-Auqb7PnS48nrYM,2025
6
+ grafeo-0.2.4.dist-info/WHEEL,sha256=iGsAlRuZHmukBRn3olcZncKJc1IZj3uaOy3gFjImPAo,110
7
+ grafeo-0.2.4.dist-info/entry_points.txt,sha256=kNAJ9wDtkgCY-nfE5IXQaf-bJi1kKRplXKdZr6xavDs,41
8
+ grafeo.libs/libgcc_s-0bf60adc.so.1,sha256=9fhX5YpiwtHdsiLMLz77Pw9jjIEGriteV7UOUrrwvpk,527681
9
+ grafeo-0.2.4.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ grafeo=grafeo.cli:main
@@ -1,7 +0,0 @@
1
- grafeo/__init__.py,sha256=k_RJXamdSIXvR1VOVd5higAea76QziQpMIKmmpp4pBc,666
2
- grafeo/grafeo.cpython-313t-aarch64-linux-musl.so,sha256=nbe8c2P-F_DJ8MTAfMRdgUAj9yBymTU12wUzbrGnEYA,4394609
3
- grafeo/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- grafeo-0.1.1.dist-info/METADATA,sha256=qQNDwTsWtNQweHIhqywgbUPbVZmamkZDXNxaguMadNQ,1634
5
- grafeo-0.1.1.dist-info/WHEEL,sha256=iGsAlRuZHmukBRn3olcZncKJc1IZj3uaOy3gFjImPAo,110
6
- grafeo.libs/libgcc_s-0bf60adc.so.1,sha256=9fhX5YpiwtHdsiLMLz77Pw9jjIEGriteV7UOUrrwvpk,527681
7
- grafeo-0.1.1.dist-info/RECORD,,
File without changes