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.
- nadata/__init__.py +15 -0
- nadata/api.py +894 -0
- nadata/cli.py +445 -0
- nadata/console.py +486 -0
- nadata/core/__init__.py +0 -0
- nadata/core/database/__init__.py +38 -0
- nadata/core/database/builtins.py +87 -0
- nadata/core/database/database.py +958 -0
- nadata/core/database/dumper.py +285 -0
- nadata/core/database/entity_id.py +142 -0
- nadata/core/database/instance.py +706 -0
- nadata/core/database/network_index.py +1142 -0
- nadata/core/database/page_manager.py +175 -0
- nadata/core/database/persistence/__init__.py +28 -0
- nadata/core/database/persistence/binary_cells.py +1141 -0
- nadata/core/database/persistence/compaction.py +97 -0
- nadata/core/database/persistence/dumper.py +798 -0
- nadata/core/database/persistence/lazy_instances.py +1537 -0
- nadata/core/database/persistence/loader.py +424 -0
- nadata/core/database/persistence/record_store.py +228 -0
- nadata/core/database/persistence/serialization.py +605 -0
- nadata/core/database/query_engine.py +222 -0
- nadata/core/database/queryset.py +508 -0
- nadata/core/database/reconciliation.py +287 -0
- nadata/core/database/slotted_page.py +190 -0
- nadata/core/database/spatial_index.py +1080 -0
- nadata/core/expressions/__init__.py +44 -0
- nadata/core/expressions/autograd.py +2909 -0
- nadata/core/expressions/builtins.py +4673 -0
- nadata/core/expressions/expression.py +2658 -0
- nadata/core/expressions/io_builtins.py +972 -0
- nadata/core/expressions/scope.py +336 -0
- nadata/core/expressions/statement.py +953 -0
- nadata/core/struct/__init__.py +14 -0
- nadata/core/struct/struct.py +218 -0
- nadata/core/struct/struct_attribute.py +100 -0
- nadata/core/struct/struct_method.py +324 -0
- nadata/core/types/__init__.py +33 -0
- nadata/core/types/bits/__init__.py +0 -0
- nadata/core/types/bits/bits.py +686 -0
- nadata/core/types/home_types/__init__.py +19 -0
- nadata/core/types/home_types/color.py +102 -0
- nadata/core/types/home_types/coordinate.py +1008 -0
- nadata/core/types/home_types/datetime.py +362 -0
- nadata/core/types/home_types/geogrid.py +92 -0
- nadata/core/types/home_types/geometry.py +1223 -0
- nadata/core/types/home_types/graph.py +0 -0
- nadata/core/types/home_types/planet.py +359 -0
- nadata/core/types/home_types/sea_level.py +98 -0
- nadata/core/types/home_types/vec.py +0 -0
- nadata/core/types/types.py +1345 -0
- nadata/core/values/__init__.py +14 -0
- nadata/core/values/values.py +1568 -0
- nadata/parser/__init__.py +0 -0
- nadata/parser/expressions/__init__.py +6 -0
- nadata/parser/expressions/dependencies.py +318 -0
- nadata/parser/expressions/expression_parser.py +993 -0
- nadata/parser/expressions/splitter.py +98 -0
- nadata/parser/expressions/statement_parser.py +1425 -0
- nadata/parser/expressions/tokenizer.py +402 -0
- nadata/parser/expressions/type_parser.py +187 -0
- nadata/parser/nadata_loader/__init__.py +8 -0
- nadata/parser/nadata_loader/loader.py +951 -0
- nadata/parser/nastruct_parser/__init__.py +0 -0
- nadata/parser/nastruct_parser/declaration.py +105 -0
- nadata/parser/nastruct_parser/imports.py +164 -0
- nadata/parser/nastruct_parser/nastruct_parser.py +209 -0
- nadata/parser/nastruct_parser/parse_attributes.py +149 -0
- nadata/parser/nastruct_parser/parse_callables.py +436 -0
- nadata/parser/nastruct_parser/parse_expression.py +59 -0
- nadata/parser/nastruct_parser/parse_structs.py +206 -0
- nadata/parser/nastruct_parser/parser_state.py +84 -0
- nadata/parser/nastruct_parser/resolver.py +1412 -0
- nadata/parser/nastruct_parser/struct_body_state.py +50 -0
- nadata/parser/nastruct_parser/tmp_structs.py +156 -0
- nadata/parser/utils/__init__.py +20 -0
- nadata/parser/utils/brackets.py +252 -0
- nadata/parser/utils/comments_remove.py +149 -0
- nadata/parser/utils/identifiers.py +121 -0
- nadata/session.py +868 -0
- nadata-0.1.0.dist-info/METADATA +191 -0
- nadata-0.1.0.dist-info/RECORD +86 -0
- nadata-0.1.0.dist-info/WHEEL +5 -0
- nadata-0.1.0.dist-info/entry_points.txt +2 -0
- nadata-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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())
|