etlplus 0.8.2__py3-none-any.whl → 0.8.4__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.
- etlplus/api/README.md +24 -26
- etlplus/cli/commands.py +361 -136
- etlplus/cli/handlers.py +286 -82
- etlplus/cli/io.py +29 -52
- etlplus/cli/main.py +13 -331
- etlplus/cli/options.py +2 -68
- etlplus/cli/state.py +9 -85
- etlplus/utils.py +0 -31
- {etlplus-0.8.2.dist-info → etlplus-0.8.4.dist-info}/METADATA +5 -2
- {etlplus-0.8.2.dist-info → etlplus-0.8.4.dist-info}/RECORD +14 -14
- {etlplus-0.8.2.dist-info → etlplus-0.8.4.dist-info}/WHEEL +0 -0
- {etlplus-0.8.2.dist-info → etlplus-0.8.4.dist-info}/entry_points.txt +0 -0
- {etlplus-0.8.2.dist-info → etlplus-0.8.4.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.8.2.dist-info → etlplus-0.8.4.dist-info}/top_level.txt +0 -0
etlplus/cli/commands.py
CHANGED
|
@@ -29,27 +29,28 @@ Notes
|
|
|
29
29
|
from __future__ import annotations
|
|
30
30
|
|
|
31
31
|
from typing import Annotated
|
|
32
|
+
from typing import Any
|
|
33
|
+
from typing import Literal
|
|
34
|
+
from typing import cast
|
|
32
35
|
|
|
33
36
|
import typer
|
|
34
37
|
|
|
35
38
|
from .. import __version__
|
|
36
|
-
from ..
|
|
39
|
+
from ..enums import FileFormat
|
|
37
40
|
from . import handlers
|
|
38
41
|
from .constants import CLI_DESCRIPTION
|
|
39
42
|
from .constants import CLI_EPILOG
|
|
40
43
|
from .constants import DATA_CONNECTORS
|
|
41
|
-
from .constants import DEFAULT_FILE_FORMAT
|
|
42
44
|
from .constants import FILE_FORMATS
|
|
45
|
+
from .io import parse_json_payload
|
|
43
46
|
from .options import typer_format_option_kwargs
|
|
44
47
|
from .state import CliState
|
|
45
48
|
from .state import ensure_state
|
|
46
|
-
from .state import format_namespace_kwargs
|
|
47
49
|
from .state import infer_resource_type_or_exit
|
|
48
50
|
from .state import infer_resource_type_soft
|
|
49
51
|
from .state import log_inferred_resource
|
|
50
52
|
from .state import optional_choice
|
|
51
53
|
from .state import resolve_resource_type
|
|
52
|
-
from .state import stateful_namespace
|
|
53
54
|
from .state import validate_choice
|
|
54
55
|
|
|
55
56
|
# SECTION: EXPORTS ========================================================== #
|
|
@@ -60,7 +61,6 @@ __all__ = ['app']
|
|
|
60
61
|
|
|
61
62
|
# SECTION: TYPE ALIASES ==================================================== #
|
|
62
63
|
|
|
63
|
-
|
|
64
64
|
OperationsOption = Annotated[
|
|
65
65
|
str,
|
|
66
66
|
typer.Option(
|
|
@@ -119,12 +119,12 @@ RenderTableOption = Annotated[
|
|
|
119
119
|
]
|
|
120
120
|
|
|
121
121
|
RenderTemplateOption = Annotated[
|
|
122
|
-
|
|
122
|
+
Literal['ddl', 'view'] | None,
|
|
123
123
|
typer.Option(
|
|
124
124
|
'--template',
|
|
125
125
|
'-t',
|
|
126
|
-
metavar='KEY
|
|
127
|
-
help='Template key (ddl/view)
|
|
126
|
+
metavar='KEY',
|
|
127
|
+
help='Template key (ddl/view).',
|
|
128
128
|
show_default=True,
|
|
129
129
|
),
|
|
130
130
|
]
|
|
@@ -149,7 +149,7 @@ RulesOption = Annotated[
|
|
|
149
149
|
]
|
|
150
150
|
|
|
151
151
|
SourceFormatOption = Annotated[
|
|
152
|
-
|
|
152
|
+
FileFormat | None,
|
|
153
153
|
typer.Option(
|
|
154
154
|
'--source-format',
|
|
155
155
|
**typer_format_option_kwargs(context='source'),
|
|
@@ -180,7 +180,7 @@ SourceOverrideOption = Annotated[
|
|
|
180
180
|
]
|
|
181
181
|
|
|
182
182
|
StdinFormatOption = Annotated[
|
|
183
|
-
|
|
183
|
+
FileFormat | None,
|
|
184
184
|
typer.Option(
|
|
185
185
|
'--source-format',
|
|
186
186
|
**typer_format_option_kwargs(context='source'),
|
|
@@ -200,7 +200,7 @@ StreamingSourceArg = Annotated[
|
|
|
200
200
|
]
|
|
201
201
|
|
|
202
202
|
TargetFormatOption = Annotated[
|
|
203
|
-
|
|
203
|
+
FileFormat | None,
|
|
204
204
|
typer.Option(
|
|
205
205
|
'--target-format',
|
|
206
206
|
**typer_format_option_kwargs(context='target'),
|
|
@@ -241,6 +241,41 @@ TargetPathOption = Annotated[
|
|
|
241
241
|
]
|
|
242
242
|
|
|
243
243
|
|
|
244
|
+
# SECTION: INTERNAL FUNCTIONS =============================================== #
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _parse_json_option(
|
|
248
|
+
value: str,
|
|
249
|
+
flag: str,
|
|
250
|
+
) -> Any:
|
|
251
|
+
"""
|
|
252
|
+
Parse JSON option values and surface a helpful CLI error.
|
|
253
|
+
|
|
254
|
+
Parameters
|
|
255
|
+
----------
|
|
256
|
+
value : str
|
|
257
|
+
The JSON string to parse.
|
|
258
|
+
flag : str
|
|
259
|
+
The CLI flag name for error messages.
|
|
260
|
+
|
|
261
|
+
Returns
|
|
262
|
+
-------
|
|
263
|
+
Any
|
|
264
|
+
The parsed JSON value.
|
|
265
|
+
|
|
266
|
+
Raises
|
|
267
|
+
------
|
|
268
|
+
typer.BadParameter
|
|
269
|
+
When the JSON is invalid.
|
|
270
|
+
"""
|
|
271
|
+
try:
|
|
272
|
+
return parse_json_payload(value)
|
|
273
|
+
except ValueError as e:
|
|
274
|
+
raise typer.BadParameter(
|
|
275
|
+
f'Invalid JSON for {flag}: {e}',
|
|
276
|
+
) from e
|
|
277
|
+
|
|
278
|
+
|
|
244
279
|
# SECTION: TYPER APP ======================================================== #
|
|
245
280
|
|
|
246
281
|
|
|
@@ -284,6 +319,25 @@ def _root(
|
|
|
284
319
|
) -> None:
|
|
285
320
|
"""
|
|
286
321
|
Seed the Typer context with runtime flags and handle root-only options.
|
|
322
|
+
|
|
323
|
+
Parameters
|
|
324
|
+
----------
|
|
325
|
+
ctx : typer.Context
|
|
326
|
+
The Typer command context.
|
|
327
|
+
version : bool, optional
|
|
328
|
+
Show the version and exit. Default is ``False``.
|
|
329
|
+
pretty : bool, optional
|
|
330
|
+
Whether to pretty-print JSON output. Default is ``True``.
|
|
331
|
+
quiet : bool, optional
|
|
332
|
+
Whether to suppress warnings and non-essential output. Default is
|
|
333
|
+
``False``.
|
|
334
|
+
verbose : bool, optional
|
|
335
|
+
Whether to emit extra diagnostics to stderr. Default is ``False``.
|
|
336
|
+
|
|
337
|
+
Raises
|
|
338
|
+
------
|
|
339
|
+
typer.Exit
|
|
340
|
+
When ``--version`` is provided or no subcommand is invoked.
|
|
287
341
|
"""
|
|
288
342
|
ctx.obj = CliState(pretty=pretty, quiet=quiet, verbose=verbose)
|
|
289
343
|
|
|
@@ -331,20 +385,47 @@ def check_cmd(
|
|
|
331
385
|
help='List data transforms',
|
|
332
386
|
),
|
|
333
387
|
) -> int:
|
|
334
|
-
"""
|
|
388
|
+
"""
|
|
389
|
+
Inspect a pipeline configuration.
|
|
390
|
+
|
|
391
|
+
Parameters
|
|
392
|
+
----------
|
|
393
|
+
ctx : typer.Context
|
|
394
|
+
The Typer context.
|
|
395
|
+
config : PipelineConfigOption
|
|
396
|
+
Path to pipeline YAML configuration file.
|
|
397
|
+
jobs : bool, optional
|
|
398
|
+
List available job names and exit. Default is ``False``.
|
|
399
|
+
pipelines : bool, optional
|
|
400
|
+
List ETL pipelines. Default is ``False``.
|
|
401
|
+
sources : bool, optional
|
|
402
|
+
List data sources. Default is ``False``.
|
|
403
|
+
summary : bool, optional
|
|
404
|
+
Show pipeline summary (name, version, sources, targets, jobs). Default
|
|
405
|
+
is ``False``.
|
|
406
|
+
targets : bool, optional
|
|
407
|
+
List data targets. Default is ``False``.
|
|
408
|
+
transforms : bool, optional
|
|
409
|
+
List data transforms. Default is ``False``.
|
|
410
|
+
|
|
411
|
+
Returns
|
|
412
|
+
-------
|
|
413
|
+
int
|
|
414
|
+
Exit code.
|
|
415
|
+
"""
|
|
335
416
|
state = ensure_state(ctx)
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
417
|
+
return int(
|
|
418
|
+
handlers.check_handler(
|
|
419
|
+
config=config,
|
|
420
|
+
jobs=jobs,
|
|
421
|
+
pipelines=pipelines,
|
|
422
|
+
sources=sources,
|
|
423
|
+
summary=summary,
|
|
424
|
+
targets=targets,
|
|
425
|
+
transforms=transforms,
|
|
426
|
+
pretty=state.pretty,
|
|
427
|
+
),
|
|
346
428
|
)
|
|
347
|
-
return int(handlers.check_handler(ns))
|
|
348
429
|
|
|
349
430
|
|
|
350
431
|
@app.command('extract')
|
|
@@ -354,7 +435,28 @@ def extract_cmd(
|
|
|
354
435
|
source_format: SourceFormatOption | None = None,
|
|
355
436
|
source_type: SourceOverrideOption | None = None,
|
|
356
437
|
) -> int:
|
|
357
|
-
"""
|
|
438
|
+
"""
|
|
439
|
+
Extract data from files, databases, or REST APIs.
|
|
440
|
+
|
|
441
|
+
Parameters
|
|
442
|
+
----------
|
|
443
|
+
ctx : typer.Context
|
|
444
|
+
The Typer context.
|
|
445
|
+
source : SourceInputArg
|
|
446
|
+
Extract from SOURCE. Use --from/--source-type to override the inferred
|
|
447
|
+
connector when needed.
|
|
448
|
+
source_format : SourceFormatOption | None, optional
|
|
449
|
+
Format of the source. Overrides filename-based inference when provided.
|
|
450
|
+
Default is ``None``.
|
|
451
|
+
source_type : SourceOverrideOption | None, optional
|
|
452
|
+
Override the inferred source type (file, database, api). Default is
|
|
453
|
+
``None``.
|
|
454
|
+
|
|
455
|
+
Returns
|
|
456
|
+
-------
|
|
457
|
+
int
|
|
458
|
+
Exit code.
|
|
459
|
+
"""
|
|
358
460
|
state = ensure_state(ctx)
|
|
359
461
|
|
|
360
462
|
source_type = optional_choice(
|
|
@@ -362,10 +464,13 @@ def extract_cmd(
|
|
|
362
464
|
DATA_CONNECTORS,
|
|
363
465
|
label='source_type',
|
|
364
466
|
)
|
|
365
|
-
source_format =
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
467
|
+
source_format = cast(
|
|
468
|
+
SourceFormatOption,
|
|
469
|
+
optional_choice(
|
|
470
|
+
source_format,
|
|
471
|
+
FILE_FORMATS,
|
|
472
|
+
label='source_format',
|
|
473
|
+
),
|
|
369
474
|
)
|
|
370
475
|
|
|
371
476
|
resolved_source = source
|
|
@@ -380,45 +485,73 @@ def extract_cmd(
|
|
|
380
485
|
resource_type=resolved_source_type,
|
|
381
486
|
)
|
|
382
487
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
source=resolved_source,
|
|
392
|
-
**format_kwargs,
|
|
488
|
+
return int(
|
|
489
|
+
handlers.extract_handler(
|
|
490
|
+
source_type=resolved_source_type,
|
|
491
|
+
source=resolved_source,
|
|
492
|
+
format_hint=source_format,
|
|
493
|
+
format_explicit=source_format is not None,
|
|
494
|
+
pretty=state.pretty,
|
|
495
|
+
),
|
|
393
496
|
)
|
|
394
|
-
return int(handlers.extract_handler(ns))
|
|
395
497
|
|
|
396
498
|
|
|
397
499
|
@app.command('load')
|
|
398
500
|
def load_cmd(
|
|
399
501
|
ctx: typer.Context,
|
|
400
502
|
target: TargetInputArg,
|
|
401
|
-
source_format: StdinFormatOption
|
|
402
|
-
target_format: TargetFormatOption
|
|
403
|
-
target_type: TargetOverrideOption
|
|
503
|
+
source_format: StdinFormatOption = None,
|
|
504
|
+
target_format: TargetFormatOption = None,
|
|
505
|
+
target_type: TargetOverrideOption = None,
|
|
404
506
|
) -> int:
|
|
405
|
-
"""
|
|
507
|
+
"""
|
|
508
|
+
Load data into a file, database, or REST API.
|
|
509
|
+
|
|
510
|
+
Parameters
|
|
511
|
+
----------
|
|
512
|
+
ctx : typer.Context
|
|
513
|
+
The Typer context.
|
|
514
|
+
target : TargetInputArg
|
|
515
|
+
Load JSON data from stdin into TARGET. Use --to/--target-type to
|
|
516
|
+
override connector inference when needed. Source data must be piped
|
|
517
|
+
into stdin.
|
|
518
|
+
source_format : StdinFormatOption, optional
|
|
519
|
+
Format of the source. Overrides filename-based inference when provided.
|
|
520
|
+
Default is ``None``.
|
|
521
|
+
target_format : TargetFormatOption, optional
|
|
522
|
+
Format of the target. Overrides filename-based inference when provided.
|
|
523
|
+
Default is ``None``.
|
|
524
|
+
target_type : TargetOverrideOption, optional
|
|
525
|
+
Override the inferred target type (file, database, api). Default is
|
|
526
|
+
``None``.
|
|
527
|
+
|
|
528
|
+
Returns
|
|
529
|
+
-------
|
|
530
|
+
int
|
|
531
|
+
Exit code.
|
|
532
|
+
"""
|
|
406
533
|
state = ensure_state(ctx)
|
|
407
534
|
|
|
408
|
-
source_format =
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
535
|
+
source_format = cast(
|
|
536
|
+
StdinFormatOption,
|
|
537
|
+
optional_choice(
|
|
538
|
+
source_format,
|
|
539
|
+
FILE_FORMATS,
|
|
540
|
+
label='source_format',
|
|
541
|
+
),
|
|
412
542
|
)
|
|
413
543
|
target_type = optional_choice(
|
|
414
544
|
target_type,
|
|
415
545
|
DATA_CONNECTORS,
|
|
416
546
|
label='target_type',
|
|
417
547
|
)
|
|
418
|
-
target_format =
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
548
|
+
target_format = cast(
|
|
549
|
+
TargetFormatOption,
|
|
550
|
+
optional_choice(
|
|
551
|
+
target_format,
|
|
552
|
+
FILE_FORMATS,
|
|
553
|
+
label='target_format',
|
|
554
|
+
),
|
|
422
555
|
)
|
|
423
556
|
|
|
424
557
|
resolved_target = target
|
|
@@ -442,20 +575,18 @@ def load_cmd(
|
|
|
442
575
|
resource_type=resolved_target_type,
|
|
443
576
|
)
|
|
444
577
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
**format_kwargs,
|
|
578
|
+
return int(
|
|
579
|
+
handlers.load_handler(
|
|
580
|
+
source=resolved_source_value,
|
|
581
|
+
target_type=resolved_target_type,
|
|
582
|
+
target=resolved_target,
|
|
583
|
+
source_format=source_format,
|
|
584
|
+
target_format=target_format,
|
|
585
|
+
format_explicit=target_format is not None,
|
|
586
|
+
output=None,
|
|
587
|
+
pretty=state.pretty,
|
|
588
|
+
),
|
|
457
589
|
)
|
|
458
|
-
return int(handlers.load_handler(ns))
|
|
459
590
|
|
|
460
591
|
|
|
461
592
|
@app.command('render')
|
|
@@ -468,19 +599,44 @@ def render_cmd(
|
|
|
468
599
|
template_path: RenderTemplatePathOption = None,
|
|
469
600
|
output: RenderOutputOption = None,
|
|
470
601
|
) -> int:
|
|
471
|
-
"""
|
|
602
|
+
"""
|
|
603
|
+
Render SQL DDL from table schemas defined in YAML/JSON configs.
|
|
604
|
+
|
|
605
|
+
Parameters
|
|
606
|
+
----------
|
|
607
|
+
ctx : typer.Context
|
|
608
|
+
The Typer context.
|
|
609
|
+
config : RenderConfigOption
|
|
610
|
+
Pipeline YAML that includes table_schemas for rendering.
|
|
611
|
+
spec : RenderSpecOption, optional
|
|
612
|
+
Standalone table spec file (.yml/.yaml/.json).
|
|
613
|
+
table : RenderTableOption, optional
|
|
614
|
+
Filter to a single table name from table_schemas.
|
|
615
|
+
template : RenderTemplateOption
|
|
616
|
+
Template key (ddl/view) or path to a Jinja template file.
|
|
617
|
+
template_path : RenderTemplatePathOption, optional
|
|
618
|
+
Explicit path to a Jinja template file (overrides template key).
|
|
619
|
+
output : RenderOutputOption, optional
|
|
620
|
+
Write rendered SQL to PATH (default: stdout).
|
|
621
|
+
|
|
622
|
+
Returns
|
|
623
|
+
-------
|
|
624
|
+
int
|
|
625
|
+
Exit code.
|
|
626
|
+
"""
|
|
472
627
|
state = ensure_state(ctx)
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
628
|
+
return int(
|
|
629
|
+
handlers.render_handler(
|
|
630
|
+
config=config,
|
|
631
|
+
spec=spec,
|
|
632
|
+
table=table,
|
|
633
|
+
template=template,
|
|
634
|
+
template_path=template_path,
|
|
635
|
+
output=output,
|
|
636
|
+
pretty=state.pretty,
|
|
637
|
+
quiet=state.quiet,
|
|
638
|
+
),
|
|
482
639
|
)
|
|
483
|
-
return int(handlers.render_handler(ns))
|
|
484
640
|
|
|
485
641
|
|
|
486
642
|
@app.command('run')
|
|
@@ -500,16 +656,34 @@ def run_cmd(
|
|
|
500
656
|
help='Name of the pipeline to run',
|
|
501
657
|
),
|
|
502
658
|
) -> int:
|
|
503
|
-
"""
|
|
659
|
+
"""
|
|
660
|
+
Execute an ETL job or pipeline from a YAML configuration.
|
|
661
|
+
|
|
662
|
+
Parameters
|
|
663
|
+
----------
|
|
664
|
+
ctx : typer.Context
|
|
665
|
+
The Typer context.
|
|
666
|
+
config : PipelineConfigOption
|
|
667
|
+
Path to pipeline YAML configuration file.
|
|
668
|
+
job : str | None, optional
|
|
669
|
+
Name of the job to run. Default is ``None``.
|
|
670
|
+
pipeline : str | None, optional
|
|
671
|
+
Name of the pipeline to run. Default is ``None``.
|
|
672
|
+
|
|
673
|
+
Returns
|
|
674
|
+
-------
|
|
675
|
+
int
|
|
676
|
+
Exit code.
|
|
677
|
+
"""
|
|
504
678
|
state = ensure_state(ctx)
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
679
|
+
return int(
|
|
680
|
+
handlers.run_handler(
|
|
681
|
+
config=config,
|
|
682
|
+
job=job,
|
|
683
|
+
pipeline=pipeline,
|
|
684
|
+
pretty=state.pretty,
|
|
685
|
+
),
|
|
511
686
|
)
|
|
512
|
-
return int(handlers.run_handler(ns))
|
|
513
687
|
|
|
514
688
|
|
|
515
689
|
@app.command('transform')
|
|
@@ -517,33 +691,65 @@ def transform_cmd(
|
|
|
517
691
|
ctx: typer.Context,
|
|
518
692
|
operations: OperationsOption = '{}',
|
|
519
693
|
source: StreamingSourceArg = '-',
|
|
520
|
-
source_format: SourceFormatOption
|
|
521
|
-
source_type: SourceOverrideOption
|
|
522
|
-
target: TargetPathOption
|
|
523
|
-
target_format: TargetFormatOption
|
|
524
|
-
target_type: TargetOverrideOption
|
|
694
|
+
source_format: SourceFormatOption = None,
|
|
695
|
+
source_type: SourceOverrideOption = None,
|
|
696
|
+
target: TargetPathOption = None,
|
|
697
|
+
target_format: TargetFormatOption = None,
|
|
698
|
+
target_type: TargetOverrideOption = None,
|
|
525
699
|
) -> int:
|
|
526
|
-
"""
|
|
700
|
+
"""
|
|
701
|
+
Transform records using JSON-described operations.
|
|
702
|
+
|
|
703
|
+
Parameters
|
|
704
|
+
----------
|
|
705
|
+
ctx : typer.Context
|
|
706
|
+
The Typer context.
|
|
707
|
+
operations : OperationsOption
|
|
708
|
+
Transformation operations as JSON string.
|
|
709
|
+
source : StreamingSourceArg
|
|
710
|
+
Data source to transform (path, JSON payload, or - for stdin).
|
|
711
|
+
source_format : SourceFormatOption, optional
|
|
712
|
+
Format of the source. Overrides filename-based inference when provided.
|
|
713
|
+
Default is ``None``.
|
|
714
|
+
source_type : SourceOverrideOption, optional
|
|
715
|
+
Override the inferred source type (file, database, api). Default is
|
|
716
|
+
``None``.
|
|
717
|
+
target : TargetPathOption, optional
|
|
718
|
+
Target file for transformed output (- for stdout). Default is ``None``.
|
|
719
|
+
target_format : TargetFormatOption, optional
|
|
720
|
+
Format of the target. Overrides filename-based inference when provided.
|
|
721
|
+
Default is ``None``.
|
|
722
|
+
target_type : TargetOverrideOption, optional
|
|
723
|
+
Override the inferred target type (file, database, api). Default is
|
|
724
|
+
``None``.
|
|
725
|
+
|
|
726
|
+
Returns
|
|
727
|
+
-------
|
|
728
|
+
int
|
|
729
|
+
Exit code.
|
|
730
|
+
"""
|
|
527
731
|
state = ensure_state(ctx)
|
|
528
732
|
|
|
529
|
-
source_format =
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
733
|
+
source_format = cast(
|
|
734
|
+
SourceFormatOption,
|
|
735
|
+
optional_choice(
|
|
736
|
+
source_format,
|
|
737
|
+
FILE_FORMATS,
|
|
738
|
+
label='source_format',
|
|
739
|
+
),
|
|
533
740
|
)
|
|
534
741
|
source_type = optional_choice(
|
|
535
742
|
source_type,
|
|
536
743
|
DATA_CONNECTORS,
|
|
537
744
|
label='source_type',
|
|
538
745
|
)
|
|
539
|
-
target_format =
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
default=DEFAULT_FILE_FORMAT,
|
|
746
|
+
target_format = cast(
|
|
747
|
+
TargetFormatOption,
|
|
748
|
+
optional_choice(
|
|
749
|
+
target_format,
|
|
750
|
+
FILE_FORMATS,
|
|
751
|
+
label='target_format',
|
|
752
|
+
),
|
|
547
753
|
)
|
|
548
754
|
target_type = optional_choice(
|
|
549
755
|
target_type,
|
|
@@ -582,19 +788,17 @@ def transform_cmd(
|
|
|
582
788
|
resource_type=resolved_target_type,
|
|
583
789
|
)
|
|
584
790
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
**target_format_kwargs,
|
|
791
|
+
return int(
|
|
792
|
+
handlers.transform_handler(
|
|
793
|
+
source=resolved_source_value,
|
|
794
|
+
operations=_parse_json_option(operations, '--operations'),
|
|
795
|
+
target=resolved_target_value,
|
|
796
|
+
source_format=source_format,
|
|
797
|
+
target_format=target_format,
|
|
798
|
+
format_explicit=target_format is not None,
|
|
799
|
+
pretty=state.pretty,
|
|
800
|
+
),
|
|
596
801
|
)
|
|
597
|
-
return int(handlers.transform_handler(ns))
|
|
598
802
|
|
|
599
803
|
|
|
600
804
|
@app.command('validate')
|
|
@@ -602,26 +806,48 @@ def validate_cmd(
|
|
|
602
806
|
ctx: typer.Context,
|
|
603
807
|
rules: RulesOption = '{}',
|
|
604
808
|
source: StreamingSourceArg = '-',
|
|
605
|
-
source_format: SourceFormatOption
|
|
606
|
-
source_type: SourceOverrideOption
|
|
607
|
-
target: TargetPathOption
|
|
809
|
+
source_format: SourceFormatOption = None,
|
|
810
|
+
source_type: SourceOverrideOption = None,
|
|
811
|
+
target: TargetPathOption = None,
|
|
608
812
|
) -> int:
|
|
609
|
-
"""
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
813
|
+
"""
|
|
814
|
+
Validate data against JSON-described rules.
|
|
815
|
+
|
|
816
|
+
Parameters
|
|
817
|
+
----------
|
|
818
|
+
ctx : typer.Context
|
|
819
|
+
The Typer context.
|
|
820
|
+
rules : RulesOption
|
|
821
|
+
Validation rules as JSON string.
|
|
822
|
+
source : StreamingSourceArg
|
|
823
|
+
Data source to validate (path, JSON payload, or - for stdin).
|
|
824
|
+
source_format : SourceFormatOption, optional
|
|
825
|
+
Format of the source. Overrides filename-based inference when provided.
|
|
826
|
+
Default is ``None``.
|
|
827
|
+
source_type : SourceOverrideOption, optional
|
|
828
|
+
Override the inferred source type (file, database, api). Default is
|
|
829
|
+
``None``.
|
|
830
|
+
target : TargetPathOption, optional
|
|
831
|
+
Target file for validated output (- for stdout). Default is ``None``.
|
|
832
|
+
|
|
833
|
+
Returns
|
|
834
|
+
-------
|
|
835
|
+
int
|
|
836
|
+
Exit code.
|
|
837
|
+
"""
|
|
838
|
+
source_format = cast(
|
|
839
|
+
SourceFormatOption,
|
|
840
|
+
optional_choice(
|
|
841
|
+
source_format,
|
|
842
|
+
FILE_FORMATS,
|
|
843
|
+
label='source_format',
|
|
844
|
+
),
|
|
614
845
|
)
|
|
615
846
|
source_type = optional_choice(
|
|
616
847
|
source_type,
|
|
617
848
|
DATA_CONNECTORS,
|
|
618
849
|
label='source_type',
|
|
619
850
|
)
|
|
620
|
-
source_format_kwargs = format_namespace_kwargs(
|
|
621
|
-
format_value=source_format,
|
|
622
|
-
default=DEFAULT_FILE_FORMAT,
|
|
623
|
-
)
|
|
624
|
-
|
|
625
851
|
state = ensure_state(ctx)
|
|
626
852
|
resolved_source_type = source_type or infer_resource_type_soft(source)
|
|
627
853
|
|
|
@@ -632,14 +858,13 @@ def validate_cmd(
|
|
|
632
858
|
resource_type=resolved_source_type,
|
|
633
859
|
)
|
|
634
860
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
861
|
+
return int(
|
|
862
|
+
handlers.validate_handler(
|
|
863
|
+
source=source,
|
|
864
|
+
rules=_parse_json_option(rules, '--rules'),
|
|
865
|
+
source_format=source_format,
|
|
866
|
+
target=target,
|
|
867
|
+
format_explicit=source_format is not None,
|
|
868
|
+
pretty=state.pretty,
|
|
869
|
+
),
|
|
644
870
|
)
|
|
645
|
-
return int(handlers.validate_handler(ns))
|