nadata 0.1.0__py3-none-any.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.
Files changed (86) hide show
  1. nadata/__init__.py +15 -0
  2. nadata/api.py +894 -0
  3. nadata/cli.py +445 -0
  4. nadata/console.py +486 -0
  5. nadata/core/__init__.py +0 -0
  6. nadata/core/database/__init__.py +38 -0
  7. nadata/core/database/builtins.py +87 -0
  8. nadata/core/database/database.py +958 -0
  9. nadata/core/database/dumper.py +285 -0
  10. nadata/core/database/entity_id.py +142 -0
  11. nadata/core/database/instance.py +706 -0
  12. nadata/core/database/network_index.py +1142 -0
  13. nadata/core/database/page_manager.py +175 -0
  14. nadata/core/database/persistence/__init__.py +28 -0
  15. nadata/core/database/persistence/binary_cells.py +1141 -0
  16. nadata/core/database/persistence/compaction.py +97 -0
  17. nadata/core/database/persistence/dumper.py +798 -0
  18. nadata/core/database/persistence/lazy_instances.py +1537 -0
  19. nadata/core/database/persistence/loader.py +424 -0
  20. nadata/core/database/persistence/record_store.py +228 -0
  21. nadata/core/database/persistence/serialization.py +605 -0
  22. nadata/core/database/query_engine.py +222 -0
  23. nadata/core/database/queryset.py +508 -0
  24. nadata/core/database/reconciliation.py +287 -0
  25. nadata/core/database/slotted_page.py +190 -0
  26. nadata/core/database/spatial_index.py +1080 -0
  27. nadata/core/expressions/__init__.py +44 -0
  28. nadata/core/expressions/autograd.py +2909 -0
  29. nadata/core/expressions/builtins.py +4673 -0
  30. nadata/core/expressions/expression.py +2658 -0
  31. nadata/core/expressions/io_builtins.py +972 -0
  32. nadata/core/expressions/scope.py +336 -0
  33. nadata/core/expressions/statement.py +953 -0
  34. nadata/core/struct/__init__.py +14 -0
  35. nadata/core/struct/struct.py +218 -0
  36. nadata/core/struct/struct_attribute.py +100 -0
  37. nadata/core/struct/struct_method.py +324 -0
  38. nadata/core/types/__init__.py +33 -0
  39. nadata/core/types/bits/__init__.py +0 -0
  40. nadata/core/types/bits/bits.py +686 -0
  41. nadata/core/types/home_types/__init__.py +19 -0
  42. nadata/core/types/home_types/color.py +102 -0
  43. nadata/core/types/home_types/coordinate.py +1008 -0
  44. nadata/core/types/home_types/datetime.py +362 -0
  45. nadata/core/types/home_types/geogrid.py +92 -0
  46. nadata/core/types/home_types/geometry.py +1223 -0
  47. nadata/core/types/home_types/graph.py +0 -0
  48. nadata/core/types/home_types/planet.py +359 -0
  49. nadata/core/types/home_types/sea_level.py +98 -0
  50. nadata/core/types/home_types/vec.py +0 -0
  51. nadata/core/types/types.py +1345 -0
  52. nadata/core/values/__init__.py +14 -0
  53. nadata/core/values/values.py +1568 -0
  54. nadata/parser/__init__.py +0 -0
  55. nadata/parser/expressions/__init__.py +6 -0
  56. nadata/parser/expressions/dependencies.py +318 -0
  57. nadata/parser/expressions/expression_parser.py +993 -0
  58. nadata/parser/expressions/splitter.py +98 -0
  59. nadata/parser/expressions/statement_parser.py +1425 -0
  60. nadata/parser/expressions/tokenizer.py +402 -0
  61. nadata/parser/expressions/type_parser.py +187 -0
  62. nadata/parser/nadata_loader/__init__.py +8 -0
  63. nadata/parser/nadata_loader/loader.py +951 -0
  64. nadata/parser/nastruct_parser/__init__.py +0 -0
  65. nadata/parser/nastruct_parser/declaration.py +105 -0
  66. nadata/parser/nastruct_parser/imports.py +164 -0
  67. nadata/parser/nastruct_parser/nastruct_parser.py +209 -0
  68. nadata/parser/nastruct_parser/parse_attributes.py +149 -0
  69. nadata/parser/nastruct_parser/parse_callables.py +436 -0
  70. nadata/parser/nastruct_parser/parse_expression.py +59 -0
  71. nadata/parser/nastruct_parser/parse_structs.py +206 -0
  72. nadata/parser/nastruct_parser/parser_state.py +84 -0
  73. nadata/parser/nastruct_parser/resolver.py +1412 -0
  74. nadata/parser/nastruct_parser/struct_body_state.py +50 -0
  75. nadata/parser/nastruct_parser/tmp_structs.py +156 -0
  76. nadata/parser/utils/__init__.py +20 -0
  77. nadata/parser/utils/brackets.py +252 -0
  78. nadata/parser/utils/comments_remove.py +149 -0
  79. nadata/parser/utils/identifiers.py +121 -0
  80. nadata/session.py +868 -0
  81. nadata-0.1.0.dist-info/METADATA +191 -0
  82. nadata-0.1.0.dist-info/RECORD +86 -0
  83. nadata-0.1.0.dist-info/WHEEL +5 -0
  84. nadata-0.1.0.dist-info/entry_points.txt +2 -0
  85. nadata-0.1.0.dist-info/licenses/LICENSE +21 -0
  86. nadata-0.1.0.dist-info/top_level.txt +1 -0
nadata/cli.py ADDED
@@ -0,0 +1,445 @@
1
+ """
2
+ Command line interface for Nadata.
3
+
4
+ Provides the run, info, and dump commands over the Nadata high level API.
5
+ """
6
+
7
+ # Import Modules
8
+ from __future__ import annotations
9
+ import argparse
10
+ import os
11
+ import sys
12
+ from typing import Any, Optional
13
+
14
+ from nadata.api import Nadata
15
+ from nadata.console import NadataConsole
16
+ from nadata.core.database.persistence import compact_database
17
+ from nadata.session import load_folder_session
18
+
19
+
20
+ def main(argv: Optional[list[str]] = None) -> int:
21
+ """
22
+ Runs the Nadata command line interface.
23
+
24
+ Args:
25
+ argv (Optional[list[str]], optional): Argument list (defaults to
26
+ sys.argv[1:]). Defaults to None.
27
+
28
+ Returns:
29
+ int: Process exit code (0 on success, 1 on error).
30
+ """
31
+
32
+ arg_parser: argparse.ArgumentParser = argparse.ArgumentParser(
33
+ prog="nadata",
34
+ description="Nadata declarative data framework command line interface.",
35
+ )
36
+
37
+ sub_parsers: argparse._SubParsersAction[argparse.ArgumentParser] = (
38
+ arg_parser.add_subparsers(dest="command", required=True)
39
+ )
40
+
41
+ run_parser: argparse.ArgumentParser = sub_parsers.add_parser(
42
+ "run",
43
+ help="Load a project (schemas and data) and execute its scripts.",
44
+ )
45
+ run_parser.add_argument("path", help="Project folder or .nastruct schema file.")
46
+ run_parser.add_argument(
47
+ "script",
48
+ nargs="?",
49
+ default=None,
50
+ help="Optional #manual script to run after the session pipeline.",
51
+ )
52
+ run_parser.add_argument(
53
+ "param",
54
+ nargs="*",
55
+ default=[],
56
+ help="Script parameters as name=value pairs.",
57
+ )
58
+ run_parser.add_argument("--db", default=None, help="World database file.")
59
+ run_parser.add_argument(
60
+ "--no-auto",
61
+ action="store_true",
62
+ help="Skip #auto scripts.",
63
+ )
64
+ run_parser.add_argument(
65
+ "--force-init",
66
+ action="store_true",
67
+ help="Re-run #init scripts even if their markers exist.",
68
+ )
69
+
70
+ info_parser: argparse.ArgumentParser = sub_parsers.add_parser(
71
+ "info",
72
+ help="Print a summary of a project or persisted database file.",
73
+ )
74
+ info_parser.add_argument("path", help="Project folder, schema file, or .ndb file.")
75
+
76
+ dump_parser: argparse.ArgumentParser = sub_parsers.add_parser(
77
+ "dump",
78
+ help="Load a project and persist it into a slotted page database file.",
79
+ )
80
+ dump_parser.add_argument("source", help="Project folder or schema file.")
81
+ dump_parser.add_argument("destination", help="Target .ndb database file.")
82
+
83
+ console_parser: argparse.ArgumentParser = sub_parsers.add_parser(
84
+ "console",
85
+ help="Open an interactive console over a loaded project or database.",
86
+ )
87
+ console_parser.add_argument(
88
+ "path",
89
+ help="Project folder, schema file, or .ndb file.",
90
+ )
91
+
92
+ open_parser: argparse.ArgumentParser = sub_parsers.add_parser(
93
+ "open",
94
+ help="Open a project folder session and drop into the console.",
95
+ )
96
+ open_parser.add_argument("folder", help="Project folder.")
97
+ open_parser.add_argument(
98
+ "--db",
99
+ default=None,
100
+ help="World database file (defaults to <folder>/nadata.ndb).",
101
+ )
102
+ open_parser.add_argument(
103
+ "--no-auto",
104
+ action="store_true",
105
+ help="Skip #auto scripts (deterministic opens for tests/CI).",
106
+ )
107
+ open_parser.add_argument(
108
+ "--force-init",
109
+ action="store_true",
110
+ help="Re-run #init scripts even if their markers exist.",
111
+ )
112
+
113
+ check_parser: argparse.ArgumentParser = sub_parsers.add_parser(
114
+ "check",
115
+ help="Run a folder session pipeline without a console (CI gate).",
116
+ )
117
+ check_parser.add_argument("folder", help="Project folder.")
118
+ check_parser.add_argument("--db", default=None, help="World database file.")
119
+ check_parser.add_argument(
120
+ "--no-auto",
121
+ action="store_true",
122
+ help="Skip #auto scripts.",
123
+ )
124
+ check_parser.add_argument(
125
+ "--force-init",
126
+ action="store_true",
127
+ help="Re-run #init scripts even if their markers exist.",
128
+ )
129
+
130
+ compact_parser: argparse.ArgumentParser = sub_parsers.add_parser(
131
+ "compact",
132
+ help="Rewrite a .ndb file into a minimal snapshot of its current state.",
133
+ )
134
+ compact_parser.add_argument("path", help="The .ndb file to compact.")
135
+ compact_parser.add_argument(
136
+ "output",
137
+ nargs="?",
138
+ default=None,
139
+ help="Optional output path (defaults to compacting in place).",
140
+ )
141
+
142
+ args = arg_parser.parse_args(argv)
143
+
144
+ try:
145
+ if args.command == "run":
146
+ return _run_command(
147
+ path=args.path,
148
+ script=args.script,
149
+ params=_parse_param_pairs(pairs=args.param),
150
+ db_path=args.db,
151
+ run_auto=not args.no_auto,
152
+ force_init=args.force_init,
153
+ )
154
+
155
+ if args.command == "open":
156
+ return _open_command(
157
+ folder=args.folder,
158
+ db_path=args.db,
159
+ run_auto=not args.no_auto,
160
+ force_init=args.force_init,
161
+ )
162
+
163
+ if args.command == "check":
164
+ return _check_command(
165
+ folder=args.folder,
166
+ db_path=args.db,
167
+ run_auto=not args.no_auto,
168
+ force_init=args.force_init,
169
+ )
170
+
171
+ if args.command == "info":
172
+ return _info_command(path=args.path)
173
+
174
+ if args.command == "console":
175
+ return _console_command(path=args.path)
176
+
177
+ if args.command == "compact":
178
+ return _compact_command(path=args.path, output=args.output)
179
+
180
+ return _dump_command(source=args.source, destination=args.destination)
181
+
182
+ except (ValueError, KeyError, TypeError, OSError) as error:
183
+ print(f"error: {error}", file=sys.stderr)
184
+
185
+ return 1
186
+
187
+
188
+ def _parse_param_pairs(pairs: list[str]) -> dict[str, str]:
189
+ """
190
+ Parses name=value CLI pairs into a dict.
191
+
192
+ Args:
193
+ pairs (list[str]): Raw 'name=value' strings.
194
+
195
+ Returns:
196
+ dict[str, str]: Parsed parameters.
197
+
198
+ Raises:
199
+ ValueError: On a pair without '='.
200
+ """
201
+
202
+ params: dict[str, str] = {}
203
+
204
+ for pair in pairs:
205
+
206
+ name, sep, value = pair.partition("=")
207
+
208
+ if not sep:
209
+ raise ValueError(
210
+ f"Malformed parameter '{pair}' (expected name=value)"
211
+ )
212
+
213
+ params[name] = value
214
+
215
+ return params
216
+
217
+
218
+ def _print_session_banner(nadata: Nadata) -> None:
219
+ """
220
+ Prints the loaded-session summary line.
221
+
222
+ Args:
223
+ nadata (Nadata): The session facade.
224
+ """
225
+
226
+ print(
227
+ f"Loaded {len(nadata.struct_names)} struct(s), "
228
+ f"{nadata.total_instance_count()} instance(s), "
229
+ f"{len(nadata.function_names)} function(s)."
230
+ )
231
+
232
+
233
+ def _run_command(
234
+ path: str,
235
+ script: Optional[str] = None,
236
+ params: Optional[dict[str, str]] = None,
237
+ db_path: Optional[str] = None,
238
+ run_auto: bool = True,
239
+ force_init: bool = False,
240
+ ) -> int:
241
+ """
242
+ Handles the run command: loads the project, executing its scripts.
243
+
244
+ With a script argument this is a headless session run: pipeline
245
+ (#init once, #auto) followed by the named #manual script. Without
246
+ one, the legacy project load applies.
247
+
248
+ Args:
249
+ path (str): Project folder or schema file.
250
+ script (Optional[str], optional): #manual script to run.
251
+ params (Optional[dict[str, str]], optional): Script parameters.
252
+ db_path (Optional[str], optional): World database file.
253
+ run_auto (bool, optional): Run #auto scripts. Defaults True.
254
+ force_init (bool, optional): Re-run #init. Defaults False.
255
+
256
+ Returns:
257
+ int: Exit code.
258
+ """
259
+
260
+ if script is None:
261
+ nadata: Nadata = Nadata.load(path=path)
262
+
263
+ _print_session_banner(nadata=nadata)
264
+
265
+ return 0
266
+
267
+ nadata = Nadata.open_folder(
268
+ folder_path=path,
269
+ db_path=db_path,
270
+ run_auto=run_auto,
271
+ force_init=force_init,
272
+ )
273
+
274
+ nadata.execute_script_file(file_path=script, params=params or {})
275
+
276
+ _print_session_banner(nadata=nadata)
277
+
278
+ return 0
279
+
280
+
281
+ def _open_command(
282
+ folder: str,
283
+ db_path: Optional[str],
284
+ run_auto: bool,
285
+ force_init: bool,
286
+ ) -> int:
287
+ """
288
+ Handles the open command: session pipeline, then the console.
289
+
290
+ Args:
291
+ folder (str): Project folder.
292
+ db_path (Optional[str]): World database file.
293
+ run_auto (bool): Run #auto scripts.
294
+ force_init (bool): Re-run #init scripts.
295
+
296
+ Returns:
297
+ int: Exit code.
298
+ """
299
+
300
+ nadata: Nadata = Nadata.open_folder(
301
+ folder_path=folder,
302
+ db_path=db_path,
303
+ run_auto=run_auto,
304
+ force_init=force_init,
305
+ )
306
+
307
+ _print_session_banner(nadata=nadata)
308
+
309
+ console: NadataConsole = NadataConsole(app=nadata)
310
+
311
+ return console.run()
312
+
313
+
314
+ def _check_command(
315
+ folder: str,
316
+ db_path: Optional[str],
317
+ run_auto: bool,
318
+ force_init: bool,
319
+ ) -> int:
320
+ """
321
+ Handles the check command: pipeline without a console (CI gate).
322
+
323
+ Args:
324
+ folder (str): Project folder.
325
+ db_path (Optional[str]): World database file.
326
+ run_auto (bool): Run #auto scripts.
327
+ force_init (bool): Re-run #init scripts.
328
+
329
+ Returns:
330
+ int: 0 when the pipeline is clean, 1 otherwise.
331
+ """
332
+
333
+ database: Any
334
+ result: Any
335
+
336
+ database, result = load_folder_session(
337
+ folder_path=folder,
338
+ db_path=db_path,
339
+ run_auto=run_auto,
340
+ force_init=force_init,
341
+ )
342
+
343
+ print(result.summary())
344
+
345
+ return 0 if result.ok() else 1
346
+
347
+
348
+ def _info_command(path: str) -> int:
349
+ """
350
+ Handles the info command: prints a database summary.
351
+
352
+ Args:
353
+ path (str): Project folder, schema file, or .ndb file.
354
+
355
+ Returns:
356
+ int: Exit code.
357
+ """
358
+
359
+ nadata: Nadata = Nadata.load(path=path)
360
+
361
+ print(nadata.summary())
362
+
363
+ return 0
364
+
365
+
366
+ def _console_command(path: str) -> int:
367
+ """
368
+ Handles the console command: loads the source and opens the REPL.
369
+
370
+ Args:
371
+ path (str): Project folder, schema file, or .ndb file.
372
+
373
+ Returns:
374
+ int: Exit code.
375
+ """
376
+
377
+ nadata: Nadata = Nadata.load(path=path)
378
+
379
+ console: NadataConsole = NadataConsole(app=nadata)
380
+
381
+ return console.run()
382
+
383
+
384
+ def _compact_command(path: str, output: Optional[str]) -> int:
385
+ """
386
+ Handles the compact command: rewrites a database file to its live state.
387
+
388
+ Args:
389
+ path (str): The .ndb file to compact.
390
+ output (Optional[str]): Optional output path (in place when None).
391
+
392
+ Returns:
393
+ int: Exit code.
394
+ """
395
+
396
+ if os.path.isdir(path):
397
+ print(
398
+ "error: cannot compact a project folder: "
399
+ "dump it first with 'nadata dump'",
400
+ file=sys.stderr,
401
+ )
402
+
403
+ return 1
404
+
405
+ documents_before: int
406
+ documents_after: int
407
+ documents_before, documents_after = compact_database(
408
+ storage_path=path,
409
+ output_path=output,
410
+ )
411
+
412
+ print(
413
+ f"Compacted '{path}': {documents_before} -> {documents_after} "
414
+ f"document(s)."
415
+ )
416
+
417
+ return 0
418
+
419
+
420
+ def _dump_command(source: str, destination: str) -> int:
421
+ """
422
+ Handles the dump command: loads the source and persists it.
423
+
424
+ Args:
425
+ source (str): Project folder or schema file.
426
+ destination (str): Target .ndb database file.
427
+
428
+ Returns:
429
+ int: Exit code.
430
+ """
431
+
432
+ nadata: Nadata = Nadata.load(path=source)
433
+
434
+ nadata.dump(storage_path=destination)
435
+
436
+ print(
437
+ f"Persisted {nadata.total_instance_count()} instance(s) "
438
+ f"across {len(nadata.struct_names)} struct(s) to '{destination}'."
439
+ )
440
+
441
+ return 0
442
+
443
+
444
+ if __name__ == "__main__":
445
+ sys.exit(main())