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/io.py
CHANGED
|
@@ -6,7 +6,6 @@ Shared I/O helpers for CLI handlers (stdin/stdout, payload hydration).
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
import argparse
|
|
10
9
|
import csv
|
|
11
10
|
import io as _io
|
|
12
11
|
import json
|
|
@@ -19,7 +18,6 @@ from typing import cast
|
|
|
19
18
|
from ..enums import FileFormat
|
|
20
19
|
from ..file import File
|
|
21
20
|
from ..types import JSONData
|
|
22
|
-
from ..utils import json_type
|
|
23
21
|
from ..utils import print_json
|
|
24
22
|
|
|
25
23
|
# SECTION: EXPORTS ========================================================== #
|
|
@@ -29,11 +27,10 @@ __all__ = [
|
|
|
29
27
|
# Functions
|
|
30
28
|
'emit_json',
|
|
31
29
|
'emit_or_write',
|
|
32
|
-
'explicit_cli_format',
|
|
33
30
|
'infer_payload_format',
|
|
34
31
|
'materialize_file_payload',
|
|
32
|
+
'parse_json_payload',
|
|
35
33
|
'parse_text_payload',
|
|
36
|
-
'presentation_flags',
|
|
37
34
|
'read_csv_rows',
|
|
38
35
|
'read_stdin_text',
|
|
39
36
|
'resolve_cli_payload',
|
|
@@ -96,34 +93,6 @@ def emit_or_write(
|
|
|
96
93
|
emit_json(data, pretty=pretty)
|
|
97
94
|
|
|
98
95
|
|
|
99
|
-
def explicit_cli_format(
|
|
100
|
-
args: argparse.Namespace,
|
|
101
|
-
) -> str | None:
|
|
102
|
-
"""
|
|
103
|
-
Return explicit format hint when provided on CLI.
|
|
104
|
-
|
|
105
|
-
Parameters
|
|
106
|
-
----------
|
|
107
|
-
args : argparse.Namespace
|
|
108
|
-
The argparse namespace containing CLI arguments.
|
|
109
|
-
|
|
110
|
-
Returns
|
|
111
|
-
-------
|
|
112
|
-
str | None
|
|
113
|
-
The explicit format hint if provided, otherwise None.
|
|
114
|
-
"""
|
|
115
|
-
if not getattr(args, '_format_explicit', False):
|
|
116
|
-
return None
|
|
117
|
-
for attr in ('format', 'target_format', 'source_format'):
|
|
118
|
-
value = getattr(args, attr, None)
|
|
119
|
-
if value is None:
|
|
120
|
-
continue
|
|
121
|
-
normalized = str(value).strip().lower()
|
|
122
|
-
if normalized:
|
|
123
|
-
return normalized
|
|
124
|
-
return None
|
|
125
|
-
|
|
126
|
-
|
|
127
96
|
def infer_payload_format(
|
|
128
97
|
text: str,
|
|
129
98
|
) -> str:
|
|
@@ -200,6 +169,33 @@ def materialize_file_payload(
|
|
|
200
169
|
return File(path, fmt).read()
|
|
201
170
|
|
|
202
171
|
|
|
172
|
+
def parse_json_payload(text: str) -> JSONData:
|
|
173
|
+
"""
|
|
174
|
+
Parse JSON text and surface a concise error when it fails.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
text : str
|
|
179
|
+
The JSON text to parse.
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
JSONData
|
|
184
|
+
The parsed JSON data.
|
|
185
|
+
|
|
186
|
+
Raises
|
|
187
|
+
------
|
|
188
|
+
ValueError
|
|
189
|
+
When the JSON text is invalid.
|
|
190
|
+
"""
|
|
191
|
+
try:
|
|
192
|
+
return cast(JSONData, json.loads(text))
|
|
193
|
+
except json.JSONDecodeError as e:
|
|
194
|
+
raise ValueError(
|
|
195
|
+
f'Invalid JSON payload: {e.msg} (pos {e.pos})',
|
|
196
|
+
) from e
|
|
197
|
+
|
|
198
|
+
|
|
203
199
|
def parse_text_payload(
|
|
204
200
|
text: str,
|
|
205
201
|
fmt: str | None,
|
|
@@ -221,32 +217,13 @@ def parse_text_payload(
|
|
|
221
217
|
"""
|
|
222
218
|
effective = (fmt or '').strip().lower() or infer_payload_format(text)
|
|
223
219
|
if effective == 'json':
|
|
224
|
-
return
|
|
220
|
+
return parse_json_payload(text)
|
|
225
221
|
if effective == 'csv':
|
|
226
222
|
reader = csv.DictReader(_io.StringIO(text))
|
|
227
223
|
return [dict(row) for row in reader]
|
|
228
224
|
return text
|
|
229
225
|
|
|
230
226
|
|
|
231
|
-
def presentation_flags(
|
|
232
|
-
args: argparse.Namespace,
|
|
233
|
-
) -> tuple[bool, bool]:
|
|
234
|
-
"""
|
|
235
|
-
Return (pretty, quiet) toggles with safe defaults.
|
|
236
|
-
|
|
237
|
-
Parameters
|
|
238
|
-
----------
|
|
239
|
-
args : argparse.Namespace
|
|
240
|
-
The argparse namespace containing CLI arguments.
|
|
241
|
-
|
|
242
|
-
Returns
|
|
243
|
-
-------
|
|
244
|
-
tuple[bool, bool]
|
|
245
|
-
A tuple containing the pretty and quiet flags.
|
|
246
|
-
"""
|
|
247
|
-
return getattr(args, 'pretty', True), getattr(args, 'quiet', False)
|
|
248
|
-
|
|
249
|
-
|
|
250
227
|
def read_csv_rows(
|
|
251
228
|
path: Path,
|
|
252
229
|
) -> list[dict[str, str]]:
|
etlplus/cli/main.py
CHANGED
|
@@ -9,31 +9,20 @@ This module exposes :func:`main` for the console script as well as
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
import argparse
|
|
13
12
|
import contextlib
|
|
14
13
|
import sys
|
|
14
|
+
import warnings
|
|
15
15
|
|
|
16
16
|
import click
|
|
17
17
|
import typer
|
|
18
18
|
|
|
19
|
-
from .. import __version__
|
|
20
|
-
from ..utils import json_type
|
|
21
|
-
from . import handlers
|
|
22
19
|
from .commands import app
|
|
23
|
-
from .constants import CLI_DESCRIPTION
|
|
24
|
-
from .constants import CLI_EPILOG
|
|
25
|
-
from .constants import DATA_CONNECTORS
|
|
26
|
-
from .constants import FILE_FORMATS
|
|
27
|
-
from .constants import PROJECT_URL
|
|
28
|
-
from .options import add_argparse_format_options
|
|
29
|
-
from .types import DataConnectorContext
|
|
30
20
|
|
|
31
21
|
# SECTION: EXPORTS ========================================================== #
|
|
32
22
|
|
|
33
23
|
|
|
34
24
|
__all__ = [
|
|
35
25
|
# Functions
|
|
36
|
-
'create_parser',
|
|
37
26
|
'main',
|
|
38
27
|
]
|
|
39
28
|
|
|
@@ -41,74 +30,6 @@ __all__ = [
|
|
|
41
30
|
# SECTION: INTERNAL FUNCTIONS =============================================== #
|
|
42
31
|
|
|
43
32
|
|
|
44
|
-
def _add_boolean_flag(
|
|
45
|
-
parser: argparse.ArgumentParser,
|
|
46
|
-
*,
|
|
47
|
-
name: str,
|
|
48
|
-
help_text: str,
|
|
49
|
-
) -> None:
|
|
50
|
-
"""Add a toggle that also supports the ``--no-`` prefix via 3.13.
|
|
51
|
-
|
|
52
|
-
Parameters
|
|
53
|
-
----------
|
|
54
|
-
parser : argparse.ArgumentParser
|
|
55
|
-
Parser receiving the flag.
|
|
56
|
-
name : str
|
|
57
|
-
Primary flag name without leading dashes.
|
|
58
|
-
help_text : str
|
|
59
|
-
Help text rendered in ``--help`` output.
|
|
60
|
-
"""
|
|
61
|
-
|
|
62
|
-
parser.add_argument(
|
|
63
|
-
f'--{name}',
|
|
64
|
-
action=argparse.BooleanOptionalAction,
|
|
65
|
-
default=False,
|
|
66
|
-
help=help_text,
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def _add_config_option(
|
|
71
|
-
parser: argparse.ArgumentParser,
|
|
72
|
-
*,
|
|
73
|
-
required: bool = True,
|
|
74
|
-
) -> None:
|
|
75
|
-
"""Attach the shared ``--config`` option used by legacy commands.
|
|
76
|
-
|
|
77
|
-
Parameters
|
|
78
|
-
----------
|
|
79
|
-
parser : argparse.ArgumentParser
|
|
80
|
-
Parser receiving the option.
|
|
81
|
-
required : bool, optional
|
|
82
|
-
Whether the flag must be provided. Defaults to ``True``.
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
parser.add_argument(
|
|
86
|
-
'--config',
|
|
87
|
-
required=required,
|
|
88
|
-
help='Path to pipeline YAML configuration file',
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _add_format_options(
|
|
93
|
-
parser: argparse.ArgumentParser,
|
|
94
|
-
*,
|
|
95
|
-
context: DataConnectorContext,
|
|
96
|
-
) -> None:
|
|
97
|
-
"""
|
|
98
|
-
Attach shared ``--source-format`` or ``--target-format`` options to
|
|
99
|
-
extract/load parsers.
|
|
100
|
-
|
|
101
|
-
Parameters
|
|
102
|
-
----------
|
|
103
|
-
parser : argparse.ArgumentParser
|
|
104
|
-
Parser to augment.
|
|
105
|
-
context : DataConnectorContext
|
|
106
|
-
Context for the format option: either ``'source'`` or ``'target'``
|
|
107
|
-
"""
|
|
108
|
-
parser.set_defaults(_format_explicit=False)
|
|
109
|
-
add_argparse_format_options(parser, context=context)
|
|
110
|
-
|
|
111
|
-
|
|
112
33
|
def _emit_context_help(
|
|
113
34
|
ctx: click.Context | None,
|
|
114
35
|
) -> bool:
|
|
@@ -199,262 +120,23 @@ def _is_unknown_command_error(
|
|
|
199
120
|
# SECTION: FUNCTIONS ======================================================== #
|
|
200
121
|
|
|
201
122
|
|
|
202
|
-
def create_parser() ->
|
|
123
|
+
def create_parser() -> object:
|
|
203
124
|
"""
|
|
204
|
-
|
|
125
|
+
Deprecated legacy entrypoint.
|
|
205
126
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
argparse.ArgumentParser
|
|
209
|
-
Parser compatible with historical ``etlplus`` entry points.
|
|
127
|
+
The argparse-based parser has been removed. Use the Typer-powered
|
|
128
|
+
``etlplus`` CLI instead (``etlplus.cli.commands.app``).
|
|
210
129
|
"""
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
parser.add_argument(
|
|
220
|
-
'-V',
|
|
221
|
-
'--version',
|
|
222
|
-
action='version',
|
|
223
|
-
version=f'%(prog)s {__version__}',
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
parser.add_argument(
|
|
227
|
-
'--pretty',
|
|
228
|
-
action=argparse.BooleanOptionalAction,
|
|
229
|
-
default=True,
|
|
230
|
-
help='Pretty-print JSON output (default: pretty).',
|
|
130
|
+
warnings.warn(
|
|
131
|
+
'create_parser is deprecated and no longer returns an argparse '
|
|
132
|
+
'parser. Use the Typer CLI entrypoint instead.',
|
|
133
|
+
DeprecationWarning,
|
|
134
|
+
stacklevel=2,
|
|
231
135
|
)
|
|
232
|
-
|
|
233
|
-
'
|
|
234
|
-
|
|
235
|
-
default=False,
|
|
236
|
-
help='Suppress warnings and non-essential output.',
|
|
237
|
-
)
|
|
238
|
-
parser.add_argument(
|
|
239
|
-
'--verbose',
|
|
240
|
-
action=argparse.BooleanOptionalAction,
|
|
241
|
-
default=False,
|
|
242
|
-
help='Emit extra diagnostics to stderr.',
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
subparsers = parser.add_subparsers(
|
|
246
|
-
dest='command',
|
|
247
|
-
help='Available commands',
|
|
248
|
-
)
|
|
249
|
-
subparsers.required = True
|
|
250
|
-
|
|
251
|
-
extract_parser = subparsers.add_parser(
|
|
252
|
-
'extract',
|
|
253
|
-
help='Extract data from sources (files, databases, REST APIs)',
|
|
254
|
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
255
|
-
)
|
|
256
|
-
extract_parser.add_argument(
|
|
257
|
-
'source_type',
|
|
258
|
-
choices=sorted(DATA_CONNECTORS),
|
|
259
|
-
help='Type of source to extract from',
|
|
260
|
-
)
|
|
261
|
-
extract_parser.add_argument(
|
|
262
|
-
'source',
|
|
263
|
-
help=(
|
|
264
|
-
'Source location (file path, database connection string, '
|
|
265
|
-
'or API URL)'
|
|
266
|
-
),
|
|
267
|
-
)
|
|
268
|
-
_add_format_options(extract_parser, context='source')
|
|
269
|
-
extract_parser.set_defaults(func=handlers.extract_handler)
|
|
270
|
-
|
|
271
|
-
validate_parser = subparsers.add_parser(
|
|
272
|
-
'validate',
|
|
273
|
-
help='Validate data from sources',
|
|
274
|
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
136
|
+
raise RuntimeError(
|
|
137
|
+
'The legacy argparse parser has been removed. Invoke the Typer-based '
|
|
138
|
+
'CLI via `etlplus` or import `etlplus.cli.commands.app`.',
|
|
275
139
|
)
|
|
276
|
-
validate_parser.add_argument(
|
|
277
|
-
'source',
|
|
278
|
-
help='Data source to validate (file path or JSON string)',
|
|
279
|
-
)
|
|
280
|
-
validate_parser.add_argument(
|
|
281
|
-
'--rules',
|
|
282
|
-
type=json_type,
|
|
283
|
-
default={},
|
|
284
|
-
help='Validation rules as JSON string',
|
|
285
|
-
)
|
|
286
|
-
validate_parser.set_defaults(func=handlers.validate_handler)
|
|
287
|
-
|
|
288
|
-
transform_parser = subparsers.add_parser(
|
|
289
|
-
'transform',
|
|
290
|
-
help='Transform data',
|
|
291
|
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
292
|
-
)
|
|
293
|
-
transform_parser.add_argument(
|
|
294
|
-
'source',
|
|
295
|
-
help='Data source to transform (file path or JSON string)',
|
|
296
|
-
)
|
|
297
|
-
transform_parser.add_argument(
|
|
298
|
-
'--operations',
|
|
299
|
-
type=json_type,
|
|
300
|
-
default={},
|
|
301
|
-
help='Transformation operations as JSON string',
|
|
302
|
-
)
|
|
303
|
-
transform_parser.add_argument(
|
|
304
|
-
'--from',
|
|
305
|
-
dest='from_',
|
|
306
|
-
choices=sorted(DATA_CONNECTORS),
|
|
307
|
-
help='Override the inferred source type (file, database, api).',
|
|
308
|
-
)
|
|
309
|
-
transform_parser.add_argument(
|
|
310
|
-
'--to',
|
|
311
|
-
dest='to',
|
|
312
|
-
choices=sorted(DATA_CONNECTORS),
|
|
313
|
-
help='Override the inferred target type (file, database, api).',
|
|
314
|
-
)
|
|
315
|
-
transform_parser.add_argument(
|
|
316
|
-
'--source-format',
|
|
317
|
-
choices=sorted(FILE_FORMATS),
|
|
318
|
-
dest='source_format',
|
|
319
|
-
help=(
|
|
320
|
-
'Input payload format when SOURCE is - or a literal payload. '
|
|
321
|
-
'File sources infer format from the extension.'
|
|
322
|
-
),
|
|
323
|
-
)
|
|
324
|
-
transform_parser.add_argument(
|
|
325
|
-
'--target-format',
|
|
326
|
-
dest='target_format',
|
|
327
|
-
choices=sorted(FILE_FORMATS),
|
|
328
|
-
help=(
|
|
329
|
-
'Output payload format '
|
|
330
|
-
'when writing to stdout or non-file targets. '
|
|
331
|
-
'File targets infer format from the extension.'
|
|
332
|
-
),
|
|
333
|
-
)
|
|
334
|
-
transform_parser.set_defaults(func=handlers.transform_handler)
|
|
335
|
-
|
|
336
|
-
load_parser = subparsers.add_parser(
|
|
337
|
-
'load',
|
|
338
|
-
help='Load data to targets (files, databases, REST APIs)',
|
|
339
|
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
340
|
-
)
|
|
341
|
-
load_parser.add_argument(
|
|
342
|
-
'source',
|
|
343
|
-
help='Data source to load (file path or JSON string)',
|
|
344
|
-
)
|
|
345
|
-
load_parser.add_argument(
|
|
346
|
-
'target_type',
|
|
347
|
-
choices=sorted(DATA_CONNECTORS),
|
|
348
|
-
help='Type of target to load to',
|
|
349
|
-
)
|
|
350
|
-
load_parser.add_argument(
|
|
351
|
-
'target',
|
|
352
|
-
help=(
|
|
353
|
-
'Target location (file path, database connection string, '
|
|
354
|
-
'or API URL)'
|
|
355
|
-
),
|
|
356
|
-
)
|
|
357
|
-
_add_format_options(load_parser, context='target')
|
|
358
|
-
load_parser.set_defaults(func=handlers.load_handler)
|
|
359
|
-
|
|
360
|
-
render_parser = subparsers.add_parser(
|
|
361
|
-
'render',
|
|
362
|
-
help='Render SQL DDL from table schema specs',
|
|
363
|
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
364
|
-
)
|
|
365
|
-
render_parser.add_argument(
|
|
366
|
-
'--config',
|
|
367
|
-
help='Pipeline YAML containing table_schemas',
|
|
368
|
-
)
|
|
369
|
-
render_parser.add_argument(
|
|
370
|
-
'-o',
|
|
371
|
-
'--output',
|
|
372
|
-
help='Write SQL to this path (stdout when omitted)',
|
|
373
|
-
)
|
|
374
|
-
render_parser.add_argument(
|
|
375
|
-
'--spec',
|
|
376
|
-
help='Standalone table spec file (.yml/.yaml/.json)',
|
|
377
|
-
)
|
|
378
|
-
render_parser.add_argument(
|
|
379
|
-
'--table',
|
|
380
|
-
help='Render only the table matching this name',
|
|
381
|
-
)
|
|
382
|
-
render_parser.add_argument(
|
|
383
|
-
'--template',
|
|
384
|
-
default='ddl',
|
|
385
|
-
help='Template key (ddl/view) or path to a Jinja template file',
|
|
386
|
-
)
|
|
387
|
-
render_parser.add_argument(
|
|
388
|
-
'--template-path',
|
|
389
|
-
dest='template_path',
|
|
390
|
-
help=(
|
|
391
|
-
'Explicit path to a Jinja template file (overrides template key).'
|
|
392
|
-
),
|
|
393
|
-
)
|
|
394
|
-
render_parser.set_defaults(func=handlers.render_handler)
|
|
395
|
-
|
|
396
|
-
check_parser = subparsers.add_parser(
|
|
397
|
-
'check',
|
|
398
|
-
help='Inspect ETL pipeline metadata',
|
|
399
|
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
400
|
-
)
|
|
401
|
-
_add_config_option(check_parser)
|
|
402
|
-
_add_boolean_flag(
|
|
403
|
-
check_parser,
|
|
404
|
-
name='jobs',
|
|
405
|
-
help_text='List ETL jobs',
|
|
406
|
-
)
|
|
407
|
-
_add_boolean_flag(
|
|
408
|
-
check_parser,
|
|
409
|
-
name='pipelines',
|
|
410
|
-
help_text='List ETL pipelines',
|
|
411
|
-
)
|
|
412
|
-
_add_boolean_flag(
|
|
413
|
-
check_parser,
|
|
414
|
-
name='sources',
|
|
415
|
-
help_text='List data sources',
|
|
416
|
-
)
|
|
417
|
-
_add_boolean_flag(
|
|
418
|
-
check_parser,
|
|
419
|
-
name='summary',
|
|
420
|
-
help_text=(
|
|
421
|
-
'Show pipeline summary (name, version, sources, targets, jobs)'
|
|
422
|
-
),
|
|
423
|
-
)
|
|
424
|
-
_add_boolean_flag(
|
|
425
|
-
check_parser,
|
|
426
|
-
name='targets',
|
|
427
|
-
help_text='List data targets',
|
|
428
|
-
)
|
|
429
|
-
_add_boolean_flag(
|
|
430
|
-
check_parser,
|
|
431
|
-
name='transforms',
|
|
432
|
-
help_text='List data transforms',
|
|
433
|
-
)
|
|
434
|
-
check_parser.set_defaults(func=handlers.check_handler)
|
|
435
|
-
|
|
436
|
-
run_parser = subparsers.add_parser(
|
|
437
|
-
'run',
|
|
438
|
-
help=(
|
|
439
|
-
'Run an ETL pipeline '
|
|
440
|
-
f'(see {PROJECT_URL}/blob/main/docs/run-module.md)'
|
|
441
|
-
),
|
|
442
|
-
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
443
|
-
)
|
|
444
|
-
_add_config_option(run_parser)
|
|
445
|
-
run_parser.add_argument(
|
|
446
|
-
'-j',
|
|
447
|
-
'--job',
|
|
448
|
-
help='Name of the job to run',
|
|
449
|
-
)
|
|
450
|
-
run_parser.add_argument(
|
|
451
|
-
'-p',
|
|
452
|
-
'--pipeline',
|
|
453
|
-
help='Name of the pipeline to run',
|
|
454
|
-
)
|
|
455
|
-
run_parser.set_defaults(func=handlers.run_handler)
|
|
456
|
-
|
|
457
|
-
return parser
|
|
458
140
|
|
|
459
141
|
|
|
460
142
|
def main(
|
etlplus/cli/options.py
CHANGED
|
@@ -1,89 +1,23 @@
|
|
|
1
1
|
"""
|
|
2
2
|
:mod:`etlplus.cli.options` module.
|
|
3
3
|
|
|
4
|
-
Shared command-line interface (CLI) option
|
|
5
|
-
|
|
4
|
+
Shared Typer helper utilities for command-line interface (CLI) option
|
|
5
|
+
configuration.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
import argparse
|
|
11
|
-
from collections.abc import Sequence
|
|
12
|
-
|
|
13
|
-
from .constants import DEFAULT_FILE_FORMAT
|
|
14
|
-
from .constants import FILE_FORMATS
|
|
15
10
|
from .types import DataConnectorContext
|
|
16
11
|
|
|
17
12
|
# SECTION: EXPORTS ========================================================== #
|
|
18
13
|
|
|
19
14
|
|
|
20
15
|
__all__ = [
|
|
21
|
-
# Classes
|
|
22
|
-
'FormatAction',
|
|
23
16
|
# Functions
|
|
24
|
-
'add_argparse_format_options',
|
|
25
17
|
'typer_format_option_kwargs',
|
|
26
18
|
]
|
|
27
19
|
|
|
28
20
|
|
|
29
|
-
# SECTION: CLASSES ========================================================== #
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class FormatAction(argparse.Action):
|
|
33
|
-
"""Record when a format override flag is provided."""
|
|
34
|
-
|
|
35
|
-
def __call__(
|
|
36
|
-
self,
|
|
37
|
-
parser: argparse.ArgumentParser,
|
|
38
|
-
namespace: argparse.Namespace,
|
|
39
|
-
values: str | Sequence[object] | None,
|
|
40
|
-
option_string: str | None = None,
|
|
41
|
-
) -> None: # pragma: no cover - argparse wiring
|
|
42
|
-
setattr(namespace, self.dest, values)
|
|
43
|
-
namespace._format_explicit = True
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# SECTION: FUNCTIONS ======================================================== #
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def add_argparse_format_options(
|
|
50
|
-
parser: argparse.ArgumentParser,
|
|
51
|
-
*,
|
|
52
|
-
context: DataConnectorContext,
|
|
53
|
-
) -> None:
|
|
54
|
-
"""
|
|
55
|
-
Attach ``--source-format`` and ``--target-format`` arguments.
|
|
56
|
-
|
|
57
|
-
Parameters
|
|
58
|
-
----------
|
|
59
|
-
parser : argparse.ArgumentParser
|
|
60
|
-
Parser receiving the options.
|
|
61
|
-
context : DataConnectorContext
|
|
62
|
-
Either ``'source'`` or ``'target'`` to tailor help text.
|
|
63
|
-
"""
|
|
64
|
-
parser.set_defaults(_format_explicit=False)
|
|
65
|
-
parser.add_argument(
|
|
66
|
-
'--source-format',
|
|
67
|
-
choices=sorted(FILE_FORMATS),
|
|
68
|
-
default=DEFAULT_FILE_FORMAT,
|
|
69
|
-
action=FormatAction,
|
|
70
|
-
help=(
|
|
71
|
-
f'Format of the {context}. Overrides filename-based inference '
|
|
72
|
-
'when provided.'
|
|
73
|
-
),
|
|
74
|
-
)
|
|
75
|
-
parser.add_argument(
|
|
76
|
-
'--target-format',
|
|
77
|
-
choices=sorted(FILE_FORMATS),
|
|
78
|
-
default=DEFAULT_FILE_FORMAT,
|
|
79
|
-
action=FormatAction,
|
|
80
|
-
help=(
|
|
81
|
-
f'Format of the {context}. Overrides filename-based inference '
|
|
82
|
-
'when provided.'
|
|
83
|
-
),
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
87
21
|
def typer_format_option_kwargs(
|
|
88
22
|
*,
|
|
89
23
|
context: DataConnectorContext,
|