etlplus 0.4.1__py3-none-any.whl → 0.4.7__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/cli/main.py CHANGED
@@ -10,10 +10,12 @@ This module exposes :func:`main` for the console script as well as
10
10
  from __future__ import annotations
11
11
 
12
12
  import argparse
13
+ import contextlib
13
14
  import sys
14
15
  from collections.abc import Sequence
15
16
  from typing import Literal
16
17
 
18
+ import click
17
19
  import typer
18
20
 
19
21
  from .. import __version__
@@ -22,7 +24,6 @@ from ..enums import FileFormat
22
24
  from ..utils import json_type
23
25
  from .app import PROJECT_URL
24
26
  from .app import app
25
- from .handlers import FORMAT_ENV_KEY
26
27
  from .handlers import cmd_extract
27
28
  from .handlers import cmd_list
28
29
  from .handlers import cmd_load
@@ -51,7 +52,9 @@ type FormatContext = Literal['source', 'target']
51
52
 
52
53
 
53
54
  class _FormatAction(argparse.Action):
54
- """Argparse action that records when ``--format`` is provided."""
55
+ """
56
+ Argparse action that records when ``--source-format`` or
57
+ ``--target-format`` is provided."""
55
58
 
56
59
  def __call__(
57
60
  self,
@@ -67,31 +70,89 @@ class _FormatAction(argparse.Action):
67
70
  # SECTION: INTERNAL FUNCTIONS =============================================== #
68
71
 
69
72
 
73
+ def _add_boolean_flag(
74
+ parser: argparse.ArgumentParser,
75
+ *,
76
+ name: str,
77
+ help_text: str,
78
+ ) -> None:
79
+ """Add a toggle that also supports the ``--no-`` prefix via 3.13.
80
+
81
+ Parameters
82
+ ----------
83
+ parser : argparse.ArgumentParser
84
+ Parser receiving the flag.
85
+ name : str
86
+ Primary flag name without leading dashes.
87
+ help_text : str
88
+ Help text rendered in ``--help`` output.
89
+ """
90
+
91
+ parser.add_argument(
92
+ f'--{name}',
93
+ action=argparse.BooleanOptionalAction,
94
+ default=False,
95
+ help=help_text,
96
+ )
97
+
98
+
99
+ def _add_config_option(
100
+ parser: argparse.ArgumentParser,
101
+ *,
102
+ required: bool = True,
103
+ ) -> None:
104
+ """Attach the shared ``--config`` option used by legacy commands.
105
+
106
+ Parameters
107
+ ----------
108
+ parser : argparse.ArgumentParser
109
+ Parser receiving the option.
110
+ required : bool, optional
111
+ Whether the flag must be provided. Defaults to ``True``.
112
+ """
113
+
114
+ parser.add_argument(
115
+ '--config',
116
+ required=required,
117
+ help='Path to pipeline YAML configuration file',
118
+ )
119
+
120
+
70
121
  def _add_format_options(
71
122
  parser: argparse.ArgumentParser,
72
123
  *,
73
124
  context: FormatContext,
74
125
  ) -> None:
75
- """Attach shared ``--format`` options to extract/load parsers."""
126
+ """
127
+ Attach shared ``--source-format`` or ``--target-format`` options to
128
+ extract/load parsers.
76
129
 
130
+ Parameters
131
+ ----------
132
+ parser : argparse.ArgumentParser
133
+ Parser to augment.
134
+ context : FormatContext
135
+ Context for the format option: either ``'source'`` or ``'target'``
136
+ """
77
137
  parser.set_defaults(_format_explicit=False)
78
138
  parser.add_argument(
79
- '--strict-format',
80
- action='store_true',
139
+ '--source-format',
140
+ choices=list(FileFormat.choices()),
141
+ default='json',
142
+ action=_FormatAction,
81
143
  help=(
82
- 'Treat providing --format for file '
83
- f'{context}s as an error (overrides environment behavior)'
144
+ f'Format of the {context}. Overrides filename-based inference '
145
+ 'when provided.'
84
146
  ),
85
147
  )
86
148
  parser.add_argument(
87
- '--format',
149
+ '--target-format',
88
150
  choices=list(FileFormat.choices()),
89
151
  default='json',
90
152
  action=_FormatAction,
91
153
  help=(
92
- f'Format of the {context} when not a file. For file {context}s '
93
- 'this option is ignored and the format is inferred from the '
94
- 'filename extension.'
154
+ f'Format of the {context}. Overrides filename-based inference '
155
+ 'when provided.'
95
156
  ),
96
157
  )
97
158
 
@@ -103,44 +164,119 @@ def _cli_description() -> str:
103
164
  '',
104
165
  ' Provide a subcommand and options. Examples:',
105
166
  '',
106
- ' etlplus extract file in.csv -o out.json',
167
+ ' etlplus extract file in.csv > out.json',
107
168
  ' etlplus validate in.json --rules \'{"required": ["id"]}\'',
108
169
  (
109
- ' etlplus transform in.json --operations '
110
- '\'{"select": ["id"]}\''
170
+ ' etlplus transform --from file in.csv --operations '
171
+ '\'{"select": ["id"]}\' --to file -o out.json'
111
172
  ),
112
- ' etlplus load in.json file out.json',
173
+ ' etlplus extract in.csv | etlplus load --to file out.json',
113
174
  '',
114
- ' Enforce error if --format is provided for files. Examples:',
175
+ ' Override format inference when extensions are misleading:',
115
176
  '',
116
- ' etlplus extract file in.csv --format csv --strict-format',
117
- (
118
- ' etlplus load in.json file out.csv --format csv '
119
- '--strict-format'
120
- ),
177
+ ' etlplus extract data.txt --source-format csv',
178
+ ' etlplus load payload.bin --target-format json',
121
179
  ],
122
180
  )
123
181
 
124
182
 
125
- def _cli_epilog(format_env_key: str) -> str:
183
+ def _cli_epilog() -> str:
126
184
  return '\n'.join(
127
185
  [
128
- 'Environment:',
129
- (
130
- f' {format_env_key} controls behavior when '
131
- '--format is provided for files.'
132
- ),
133
- ' Values:',
134
- ' - error|fail|strict: treat as error',
135
- ' - warn (default): print a warning',
136
- ' - ignore|silent: no message',
137
- '',
138
- 'Note:',
139
- ' --strict-format overrides the environment behavior.',
186
+ 'Tip:',
187
+ ' --source-format and --target-format override format '
188
+ 'inference based on filename extensions when needed.',
140
189
  ],
141
190
  )
142
191
 
143
192
 
193
+ def _emit_context_help(
194
+ ctx: click.Context | None,
195
+ ) -> bool:
196
+ """
197
+ Mirror Click help output for the provided context onto stderr.
198
+
199
+ Parameters
200
+ ----------
201
+ ctx : click.Context | None
202
+ The Click context to emit help for.
203
+
204
+ Returns
205
+ -------
206
+ bool
207
+ ``True`` when help was emitted, ``False`` when ``ctx`` was ``None``.
208
+ """
209
+ if ctx is None:
210
+ return False
211
+
212
+ with contextlib.redirect_stdout(sys.stderr):
213
+ ctx.get_help()
214
+ return True
215
+
216
+
217
+ def _emit_root_help(
218
+ command: click.Command,
219
+ ) -> None:
220
+ """
221
+ Print the root ``etlplus`` help text to stderr.
222
+
223
+ Parameters
224
+ ----------
225
+ command : click.Command
226
+ The root Typer/Click command.
227
+ """
228
+ ctx = command.make_context('etlplus', [], resilient_parsing=True)
229
+ try:
230
+ _emit_context_help(ctx)
231
+ finally:
232
+ ctx.close()
233
+
234
+
235
+ def _is_illegal_option_error(
236
+ exc: click.exceptions.UsageError,
237
+ ) -> bool:
238
+ """
239
+ Return ``True`` when usage errors stem from invalid options.
240
+
241
+ Parameters
242
+ ----------
243
+ exc : click.exceptions.UsageError
244
+ The usage error to inspect.
245
+
246
+ Returns
247
+ -------
248
+ bool
249
+ ``True`` when the error indicates illegal options.
250
+ """
251
+ return isinstance(
252
+ exc,
253
+ (
254
+ click.exceptions.BadOptionUsage,
255
+ click.exceptions.NoSuchOption,
256
+ ),
257
+ )
258
+
259
+
260
+ def _is_unknown_command_error(
261
+ exc: click.exceptions.UsageError,
262
+ ) -> bool:
263
+ """
264
+ Return ``True`` when a :class:`UsageError` indicates bad subcommand.
265
+
266
+ Parameters
267
+ ----------
268
+ exc : click.exceptions.UsageError
269
+ The usage error to inspect.
270
+
271
+ Returns
272
+ -------
273
+ bool
274
+ ``True`` when the error indicates an unknown command.
275
+ """
276
+ message = getattr(exc, 'message', None) or str(exc)
277
+ return message.startswith('No such command ')
278
+
279
+
144
280
  # SECTION: FUNCTIONS ======================================================== #
145
281
 
146
282
 
@@ -157,7 +293,7 @@ def create_parser() -> argparse.ArgumentParser:
157
293
  parser = argparse.ArgumentParser(
158
294
  prog='etlplus',
159
295
  description=_cli_description(),
160
- epilog=_cli_epilog(FORMAT_ENV_KEY),
296
+ epilog=_cli_epilog(),
161
297
  formatter_class=argparse.RawDescriptionHelpFormatter,
162
298
  )
163
299
 
@@ -172,6 +308,7 @@ def create_parser() -> argparse.ArgumentParser:
172
308
  dest='command',
173
309
  help='Available commands',
174
310
  )
311
+ subparsers.required = True
175
312
 
176
313
  extract_parser = subparsers.add_parser(
177
314
  'extract',
@@ -190,11 +327,6 @@ def create_parser() -> argparse.ArgumentParser:
190
327
  'or API URL)'
191
328
  ),
192
329
  )
193
- extract_parser.add_argument(
194
- '-o',
195
- '--output',
196
- help='Output file to save extracted data (JSON format)',
197
- )
198
330
  _add_format_options(extract_parser, context='source')
199
331
  extract_parser.set_defaults(func=cmd_extract)
200
332
 
@@ -231,9 +363,35 @@ def create_parser() -> argparse.ArgumentParser:
231
363
  help='Transformation operations as JSON string',
232
364
  )
233
365
  transform_parser.add_argument(
234
- '-o',
235
- '--output',
236
- help='Output file to save transformed data',
366
+ '--from',
367
+ dest='from_',
368
+ choices=list(DataConnectorType.choices()),
369
+ help='Override the inferred source type (file, database, api).',
370
+ )
371
+ transform_parser.add_argument(
372
+ '--to',
373
+ dest='to',
374
+ choices=list(DataConnectorType.choices()),
375
+ help='Override the inferred target type (file, database, api).',
376
+ )
377
+ transform_parser.add_argument(
378
+ '--source-format',
379
+ choices=list(FileFormat.choices()),
380
+ dest='source_format',
381
+ help=(
382
+ 'Input payload format when SOURCE is - or a literal payload. '
383
+ 'File sources infer format from the extension.'
384
+ ),
385
+ )
386
+ transform_parser.add_argument(
387
+ '--target-format',
388
+ dest='target_format',
389
+ choices=list(FileFormat.choices()),
390
+ help=(
391
+ 'Output payload format '
392
+ 'when writing to stdout or non-file targets. '
393
+ 'File targets infer format from the extension.'
394
+ ),
237
395
  )
238
396
  transform_parser.set_defaults(func=cmd_transform)
239
397
 
@@ -269,11 +427,7 @@ def create_parser() -> argparse.ArgumentParser:
269
427
  ),
270
428
  formatter_class=argparse.ArgumentDefaultsHelpFormatter,
271
429
  )
272
- pipe_parser.add_argument(
273
- '--config',
274
- required=True,
275
- help='Path to pipeline YAML configuration file',
276
- )
430
+ _add_config_option(pipe_parser)
277
431
  pipe_parser.add_argument(
278
432
  '--list',
279
433
  action='store_true',
@@ -291,30 +445,26 @@ def create_parser() -> argparse.ArgumentParser:
291
445
  help='List ETL pipeline metadata',
292
446
  formatter_class=argparse.ArgumentDefaultsHelpFormatter,
293
447
  )
294
- list_parser.add_argument(
295
- '--config',
296
- required=True,
297
- help='Path to pipeline YAML configuration file',
448
+ _add_config_option(list_parser)
449
+ _add_boolean_flag(
450
+ list_parser,
451
+ name='pipelines',
452
+ help_text='List ETL pipelines',
298
453
  )
299
- list_parser.add_argument(
300
- '--pipelines',
301
- action='store_true',
302
- help='List ETL pipelines',
303
- )
304
- list_parser.add_argument(
305
- '--sources',
306
- action='store_true',
307
- help='List data sources',
454
+ _add_boolean_flag(
455
+ list_parser,
456
+ name='sources',
457
+ help_text='List data sources',
308
458
  )
309
- list_parser.add_argument(
310
- '--targets',
311
- action='store_true',
312
- help='List data targets',
459
+ _add_boolean_flag(
460
+ list_parser,
461
+ name='targets',
462
+ help_text='List data targets',
313
463
  )
314
- list_parser.add_argument(
315
- '--transforms',
316
- action='store_true',
317
- help='List data transforms',
464
+ _add_boolean_flag(
465
+ list_parser,
466
+ name='transforms',
467
+ help_text='List data transforms',
318
468
  )
319
469
  list_parser.set_defaults(func=cmd_list)
320
470
 
@@ -326,11 +476,7 @@ def create_parser() -> argparse.ArgumentParser:
326
476
  ),
327
477
  formatter_class=argparse.ArgumentDefaultsHelpFormatter,
328
478
  )
329
- run_parser.add_argument(
330
- '--config',
331
- required=True,
332
- help='Path to pipeline YAML configuration file',
333
- )
479
+ _add_config_option(run_parser)
334
480
  run_parser.add_argument(
335
481
  '-j',
336
482
  '--job',
@@ -365,6 +511,9 @@ def main(
365
511
 
366
512
  Raises
367
513
  ------
514
+ click.exceptions.UsageError
515
+ Re-raises Typer/Click usage errors after printing help for unknown
516
+ commands.
368
517
  SystemExit
369
518
  Re-raises SystemExit exceptions to preserve exit codes.
370
519
 
@@ -385,6 +534,19 @@ def main(
385
534
  )
386
535
  return int(result or 0)
387
536
 
537
+ except click.exceptions.UsageError as exc:
538
+ if _is_unknown_command_error(exc):
539
+ typer.echo(f'Error: {exc}', err=True)
540
+ _emit_root_help(command)
541
+ return int(getattr(exc, 'exit_code', 2))
542
+ if _is_illegal_option_error(exc):
543
+ typer.echo(f'Error: {exc}', err=True)
544
+ if not _emit_context_help(exc.ctx):
545
+ _emit_root_help(command)
546
+ return int(getattr(exc, 'exit_code', 2))
547
+
548
+ raise
549
+
388
550
  except typer.Exit as exc:
389
551
  return int(exc.exit_code)
390
552
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.4.1
3
+ Version: 0.4.7
4
4
  Summary: A Swiss Army knife for simple ETL operations
5
5
  Home-page: https://github.com/Dagitali/ETLPlus
6
6
  Author: ETLPlus Team
@@ -67,7 +67,7 @@ package and command-line interface for data extraction, validation, transformati
67
67
  - [Load Data](#load-data)
68
68
  - [Python API](#python-api)
69
69
  - [Complete ETL Pipeline Example](#complete-etl-pipeline-example)
70
- - [Environment Variables](#environment-variables)
70
+ - [Format Overrides](#format-overrides)
71
71
  - [Transformation Operations](#transformation-operations)
72
72
  - [Filter Operations](#filter-operations)
73
73
  - [Aggregation Functions](#aggregation-functions)
@@ -79,6 +79,8 @@ package and command-line interface for data extraction, validation, transformati
79
79
  - [Test Layers](#test-layers)
80
80
  - [Code Coverage](#code-coverage)
81
81
  - [Linting](#linting)
82
+ - [Updating Demo Snippets](#updating-demo-snippets)
83
+ - [Releasing to PyPI](#releasing-to-pypi)
82
84
  - [Links](#links)
83
85
  - [License](#license)
84
86
  - [Contributing](#contributing)
@@ -169,9 +171,9 @@ etlplus --version
169
171
 
170
172
  #### Extract Data
171
173
 
172
- Note: For file sources, the format is inferred from the filename extension; the `--format` option is
173
- ignored. To treat passing `--format` as an error for file sources, either set
174
- `ETLPLUS_FORMAT_BEHAVIOR=error` or pass the CLI flag `--strict-format`.
174
+ Note: For file sources, the format is normally inferred from the filename extension. Use
175
+ `--source-format` to override inference when a file lacks an extension or when you want to force a
176
+ specific parser.
175
177
 
176
178
  Extract from JSON file:
177
179
  ```bash
@@ -212,6 +214,20 @@ etlplus validate examples/data/sample.json --rules '{"email": {"type": "string",
212
214
 
213
215
  #### Transform Data
214
216
 
217
+ When piping data through `etlplus transform`, use `--source-format` whenever the SOURCE argument is
218
+ `-` or a literal payload, mirroring the `etlplus extract` semantics. Use `--target-format` to
219
+ control the emitted format for stdout or other non-file outputs, just like `etlplus load`. File
220
+ paths continue to infer formats from their extensions. Use `--from` to override the inferred source
221
+ connector type and `--to` to override the inferred target connector type, matching the `etlplus
222
+ extract`/`etlplus load` behavior.
223
+
224
+ Transform file inputs while overriding connector types:
225
+ ```bash
226
+ etlplus transform --from file examples/data/sample.json \
227
+ --operations '{"select": ["name", "email"]}' \
228
+ --to file -o temp/selected_output.json
229
+ ```
230
+
215
231
  Filter and select fields:
216
232
  ```bash
217
233
  etlplus transform '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]' \
@@ -235,19 +251,24 @@ etlplus transform examples/data/sample.json --operations '{"map": {"name": "new_
235
251
 
236
252
  #### Load Data
237
253
 
254
+ `etlplus load` consumes JSON from stdin; provide only the target argument plus optional flags.
255
+
238
256
  Load to JSON file:
239
257
  ```bash
240
- etlplus load '{"name": "John", "age": 30}' file temp/sample_output.json
258
+ etlplus extract file examples/data/sample.json \
259
+ | etlplus load --to file temp/sample_output.json
241
260
  ```
242
261
 
243
262
  Load to CSV file:
244
263
  ```bash
245
- etlplus load '[{"name": "John", "age": 30}]' file temp/sample_output.csv
264
+ etlplus extract file examples/data/sample.csv \
265
+ | etlplus load --to file temp/sample_output.csv
246
266
  ```
247
267
 
248
268
  Load to REST API:
249
269
  ```bash
250
- etlplus load examples/data/sample.json api https://api.example.com/endpoint
270
+ cat examples/data/sample.json \
271
+ | etlplus load --to api https://api.example.com/endpoint
251
272
  ```
252
273
 
253
274
  ### Python API
@@ -301,41 +322,28 @@ etlplus validate temp/sample_transformed.json \
301
322
  --rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}'
302
323
 
303
324
  # 4. Load to CSV
304
- etlplus load temp/sample_transformed.json file temp/sample_output.csv
325
+ cat temp/sample_transformed.json \
326
+ | etlplus load --to temp/sample_output.csv
305
327
  ```
306
328
 
307
- ### Environment Variables
308
-
309
- ETLPlus honors a small number of environment toggles to refine CLI behavior:
329
+ ### Format Overrides
310
330
 
311
- - `ETLPLUS_FORMAT_BEHAVIOR`: controls what happens when `--format` is provided for
312
- file sources or targets (extract/load) where the format is inferred from the
313
- filename extension.
314
- - `error|fail|strict`: treat as error (non-zero exit)
315
- - `warn` (default): print a warning to stderr
316
- - `ignore|silent`: no message
317
- - Precedence: the CLI flag `--strict-format` overrides the environment.
331
+ `--source-format` and `--target-format` override whichever format would normally be inferred from a
332
+ file extension. This is useful when an input lacks an extension (for example, `records.txt` that
333
+ actually contains CSV) or when you intentionally want to treat a file as another format.
318
334
 
319
335
  Examples (zsh):
320
336
 
321
337
  ```zsh
322
- # Warn (default)
323
- etlplus extract file data.csv --format csv
324
- etlplus load data.json file out.csv --format csv
325
-
326
- # Enforce error via environment
327
- ETLPLUS_FORMAT_BEHAVIOR=error \
328
- etlplus extract file data.csv --format csv
329
- ETLPLUS_FORMAT_BEHAVIOR=error \
330
- etlplus load data.json file out.csv --format csv
331
-
332
- # Equivalent strict behavior via flag (overrides environment)
333
- etlplus extract file data.csv --format csv --strict-format
334
- etlplus load data.json file out.csv --format csv --strict-format
335
-
336
- # Recommended: rely on extension, no --format needed for files
337
- etlplus extract file data.csv
338
- etlplus load data.json file out.csv
338
+ # Force CSV parsing for an extension-less file
339
+ etlplus extract --from file data.txt --source-format csv
340
+
341
+ # Write CSV to a file without the .csv suffix
342
+ etlplus load --to file output.bin --target-format csv < data.json
343
+
344
+ # Leave the flags off when extensions already match the desired format
345
+ etlplus extract --from file data.csv
346
+ etlplus load --to file data.json < data.json
339
347
  ```
340
348
 
341
349
  ## Transformation Operations
@@ -31,9 +31,9 @@ etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-
31
31
  etlplus/api/rate_limiting/config.py,sha256=2b4wIynblN-1EyMqI4aXa71SljzSjXYh5N1Nngr3jOg,9406
32
32
  etlplus/api/rate_limiting/rate_limiter.py,sha256=Uxozqd_Ej5Lsj-M-mLT2WexChgWh7x35_YP10yqYPQA,7159
33
33
  etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
34
- etlplus/cli/app.py,sha256=xNsre2GDErbQv3Cwd3hwRHpkkoh-ZYIbBLrnKrFs0VI,25268
35
- etlplus/cli/handlers.py,sha256=QlZTTg9R493J2R4z81yRS1Zt3XEGeiqmszGT_GT2c5A,16059
36
- etlplus/cli/main.py,sha256=nofNgGqfStGdyNLV6O8k8XguRNhMIm92EhuO68nGPOE,11148
34
+ etlplus/cli/app.py,sha256=pc9VDUb3Qc8u5-XyDrHJkrSR9D3bq4e9zLbaD8KzyfY,32618
35
+ etlplus/cli/handlers.py,sha256=aI_ZlnJCGGkVnVJJPhmPRCXc31MxtLaOeqqJoo3ci48,15816
36
+ etlplus/cli/main.py,sha256=LaE3q4jbQ8KTTvs4D_khpOdlTKrdaPmxB28WML6hnLg,15059
37
37
  etlplus/config/__init__.py,sha256=VZWzOg7d2YR9NT6UwKTv44yf2FRUMjTHynkm1Dl5Qzo,1486
38
38
  etlplus/config/connector.py,sha256=0-TIwevHbKRHVmucvyGpPd-3tB1dKHB-dj0yJ6kq5eY,9809
39
39
  etlplus/config/jobs.py,sha256=hmzRCqt0OvCEZZR4ONKrd3lvSv0OmayjLc4yOBk3ug8,7399
@@ -43,9 +43,9 @@ etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
43
43
  etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
44
44
  etlplus/validation/__init__.py,sha256=Pe5Xg1_EA4uiNZGYu5WTF3j7odjmyxnAJ8rcioaplSQ,1254
45
45
  etlplus/validation/utils.py,sha256=Mtqg449VIke0ziy_wd2r6yrwJzQkA1iulZC87FzXMjo,10201
46
- etlplus-0.4.1.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
47
- etlplus-0.4.1.dist-info/METADATA,sha256=PGl9CCUvlxyqz_zNfG_ibDQ5iOxib70Vas1xeWURgA4,16758
48
- etlplus-0.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
- etlplus-0.4.1.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
50
- etlplus-0.4.1.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
51
- etlplus-0.4.1.dist-info/RECORD,,
46
+ etlplus-0.4.7.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
47
+ etlplus-0.4.7.dist-info/METADATA,sha256=zD88Gnu6IAFvbIR_L--yZE5EN2VpEK-Q3B1CZypmF9I,17278
48
+ etlplus-0.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
+ etlplus-0.4.7.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
50
+ etlplus-0.4.7.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
51
+ etlplus-0.4.7.dist-info/RECORD,,