etlplus 0.5.2__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 (55) hide show
  1. etlplus/__init__.py +43 -0
  2. etlplus/__main__.py +22 -0
  3. etlplus/__version__.py +14 -0
  4. etlplus/api/README.md +237 -0
  5. etlplus/api/__init__.py +136 -0
  6. etlplus/api/auth.py +432 -0
  7. etlplus/api/config.py +633 -0
  8. etlplus/api/endpoint_client.py +885 -0
  9. etlplus/api/errors.py +170 -0
  10. etlplus/api/pagination/__init__.py +47 -0
  11. etlplus/api/pagination/client.py +188 -0
  12. etlplus/api/pagination/config.py +440 -0
  13. etlplus/api/pagination/paginator.py +775 -0
  14. etlplus/api/rate_limiting/__init__.py +38 -0
  15. etlplus/api/rate_limiting/config.py +343 -0
  16. etlplus/api/rate_limiting/rate_limiter.py +266 -0
  17. etlplus/api/request_manager.py +589 -0
  18. etlplus/api/retry_manager.py +430 -0
  19. etlplus/api/transport.py +325 -0
  20. etlplus/api/types.py +172 -0
  21. etlplus/cli/__init__.py +15 -0
  22. etlplus/cli/app.py +1367 -0
  23. etlplus/cli/handlers.py +771 -0
  24. etlplus/cli/main.py +616 -0
  25. etlplus/config/__init__.py +56 -0
  26. etlplus/config/connector.py +372 -0
  27. etlplus/config/jobs.py +311 -0
  28. etlplus/config/pipeline.py +339 -0
  29. etlplus/config/profile.py +78 -0
  30. etlplus/config/types.py +204 -0
  31. etlplus/config/utils.py +120 -0
  32. etlplus/ddl.py +197 -0
  33. etlplus/enums.py +414 -0
  34. etlplus/extract.py +218 -0
  35. etlplus/file.py +657 -0
  36. etlplus/load.py +336 -0
  37. etlplus/mixins.py +62 -0
  38. etlplus/py.typed +0 -0
  39. etlplus/run.py +368 -0
  40. etlplus/run_helpers.py +843 -0
  41. etlplus/templates/__init__.py +5 -0
  42. etlplus/templates/ddl.sql.j2 +128 -0
  43. etlplus/templates/view.sql.j2 +69 -0
  44. etlplus/transform.py +1049 -0
  45. etlplus/types.py +227 -0
  46. etlplus/utils.py +638 -0
  47. etlplus/validate.py +493 -0
  48. etlplus/validation/__init__.py +44 -0
  49. etlplus/validation/utils.py +389 -0
  50. etlplus-0.5.2.dist-info/METADATA +608 -0
  51. etlplus-0.5.2.dist-info/RECORD +55 -0
  52. etlplus-0.5.2.dist-info/WHEEL +5 -0
  53. etlplus-0.5.2.dist-info/entry_points.txt +2 -0
  54. etlplus-0.5.2.dist-info/licenses/LICENSE +21 -0
  55. etlplus-0.5.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,608 @@
1
+ Metadata-Version: 2.4
2
+ Name: etlplus
3
+ Version: 0.5.2
4
+ Summary: A Swiss Army knife for simple ETL operations
5
+ Home-page: https://github.com/Dagitali/ETLPlus
6
+ Author: ETLPlus Team
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/Dagitali/ETLPlus
9
+ Project-URL: Repository, https://github.com/Dagitali/ETLPlus
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Python: >=3.13,<3.15
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: jinja2>=3.1.6
21
+ Requires-Dist: pyodbc>=5.3.0
22
+ Requires-Dist: python-dotenv>=1.2.1
23
+ Requires-Dist: pandas>=2.3.3
24
+ Requires-Dist: requests>=2.32.5
25
+ Requires-Dist: typer>=0.21.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: black>=25.9.0; extra == "dev"
28
+ Requires-Dist: build>=1.2.2; extra == "dev"
29
+ Requires-Dist: flake8>=7.3.0; extra == "dev"
30
+ Requires-Dist: PyYAML>=6.0.3; extra == "dev"
31
+ Requires-Dist: pydoclint>=0.8.1; extra == "dev"
32
+ Requires-Dist: pydocstyle>=6.3.0; extra == "dev"
33
+ Requires-Dist: pytest>=8.4.2; extra == "dev"
34
+ Requires-Dist: pytest-cov>=7.0.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.14.4; extra == "dev"
36
+ Provides-Extra: docs
37
+ Requires-Dist: sphinx>=4.0.0; extra == "docs"
38
+ Requires-Dist: sphinx-rtd-theme>=1.0.0; extra == "docs"
39
+ Dynamic: home-page
40
+ Dynamic: license-file
41
+ Dynamic: requires-python
42
+
43
+ # ETLPlus
44
+
45
+ [![PyPI](https://img.shields.io/pypi/v/etlplus.svg)][PyPI package]
46
+ [![Release](https://img.shields.io/github/v/release/Dagitali/ETLPlus)][GitHub release]
47
+ [![Python](https://img.shields.io/pypi/pyversions/etlplus)][PyPI package]
48
+ [![License](https://img.shields.io/github/license/Dagitali/ETLPlus.svg)](LICENSE)
49
+ [![CI](https://github.com/Dagitali/ETLPlus/actions/workflows/ci.yml/badge.svg?branch=main)][GitHub Actions CI workflow]
50
+ [![Coverage](https://img.shields.io/codecov/c/github/Dagitali/ETLPlus?branch=main)][Codecov project]
51
+ [![Issues](https://img.shields.io/github/issues/Dagitali/ETLPlus)][GitHub issues]
52
+ [![PRs](https://img.shields.io/github/issues-pr/Dagitali/ETLPlus)][GitHub PRs]
53
+ [![GitHub contributors](https://img.shields.io/github/contributors/Dagitali/ETLPlus)][GitHub contributors]
54
+
55
+ ETLPlus is a veritable Swiss Army knife for enabling simple ETL operations, offering both a Python
56
+ package and command-line interface for data extraction, validation, transformation, and loading.
57
+
58
+ - [ETLPlus](#etlplus)
59
+ - [Features](#features)
60
+ - [Installation](#installation)
61
+ - [Quickstart](#quickstart)
62
+ - [Usage](#usage)
63
+ - [Command Line Interface](#command-line-interface)
64
+ - [Inspect Pipelines](#inspect-pipelines)
65
+ - [Render SQL DDL](#render-sql-ddl)
66
+ - [Extract Data](#extract-data)
67
+ - [Validate Data](#validate-data)
68
+ - [Transform Data](#transform-data)
69
+ - [Load Data](#load-data)
70
+ - [Python API](#python-api)
71
+ - [Complete ETL Pipeline Example](#complete-etl-pipeline-example)
72
+ - [Format Overrides](#format-overrides)
73
+ - [Transformation Operations](#transformation-operations)
74
+ - [Filter Operations](#filter-operations)
75
+ - [Aggregation Functions](#aggregation-functions)
76
+ - [Validation Rules](#validation-rules)
77
+ - [Development](#development)
78
+ - [API Client Docs](#api-client-docs)
79
+ - [Runner Internals and Connectors](#runner-internals-and-connectors)
80
+ - [Running Tests](#running-tests)
81
+ - [Test Layers](#test-layers)
82
+ - [Code Coverage](#code-coverage)
83
+ - [Linting](#linting)
84
+ - [Updating Demo Snippets](#updating-demo-snippets)
85
+ - [Releasing to PyPI](#releasing-to-pypi)
86
+ - [Links](#links)
87
+ - [License](#license)
88
+ - [Contributing](#contributing)
89
+ - [Acknowledgments](#acknowledgments)
90
+
91
+ ## Features
92
+
93
+ - **Extract** data from multiple sources:
94
+ - Files (CSV, JSON, XML, YAML)
95
+ - Databases (connection string support)
96
+ - REST APIs (GET)
97
+
98
+ - **Validate** data with flexible rules:
99
+ - Type checking
100
+ - Required fields
101
+ - Value ranges (min/max)
102
+ - String length constraints
103
+ - Pattern matching
104
+ - Enum validation
105
+
106
+ - **Transform** data with powerful operations:
107
+ - Filter records
108
+ - Map/rename fields
109
+ - Select specific fields
110
+ - Sort data
111
+ - Aggregate functions (avg, count, max, min, sum)
112
+
113
+ - **Load** data to multiple targets:
114
+ - Files (CSV, JSON, XML, YAML)
115
+ - Databases (connection string support)
116
+ - REST APIs (PATCH, POST, PUT)
117
+
118
+ ## Installation
119
+
120
+ ```bash
121
+ pip install etlplus
122
+ ```
123
+
124
+ For development:
125
+
126
+ ```bash
127
+ pip install -e ".[dev]"
128
+ ```
129
+
130
+ ## Quickstart
131
+
132
+ Get up and running in under a minute.
133
+
134
+ [Command line interface](#command-line-interface):
135
+
136
+ ```bash
137
+ # Inspect help and version
138
+ etlplus --help
139
+ etlplus --version
140
+
141
+ # One-liner: extract CSV, filter, select, and write JSON
142
+ etlplus extract file examples/data/sample.csv \
143
+ | etlplus transform - --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
144
+ -o temp/sample_output.json
145
+ ```
146
+
147
+ [Python API](#python-api):
148
+
149
+ ```python
150
+ from etlplus import extract, transform, validate, load
151
+
152
+ data = extract("file", "input.csv")
153
+ ops = {"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}
154
+ filtered = transform(data, ops)
155
+ rules = {"name": {"type": "string", "required": True}, "email": {"type": "string", "required": True}}
156
+ assert validate(filtered, rules)["valid"]
157
+ load(filtered, "file", "temp/sample_output.json", file_format="json")
158
+ ```
159
+
160
+ ## Usage
161
+
162
+ ### Command Line Interface
163
+
164
+ ETLPlus provides a powerful CLI for ETL operations:
165
+
166
+ ```bash
167
+ # Show help
168
+ etlplus --help
169
+
170
+ # Show version
171
+ etlplus --version
172
+ ```
173
+
174
+ #### Inspect Pipelines
175
+
176
+ Use `etlplus list` to explore pipeline YAML definitions without running them. The command can print
177
+ job names, summarize configured sources and targets, or drill into specific sections.
178
+
179
+ List jobs and show a pipeline summary:
180
+ ```bash
181
+ etlplus list --config examples/configs/pipeline.yml --jobs
182
+ etlplus list --config examples/configs/pipeline.yml --summary
183
+ ```
184
+
185
+ Show sources or transforms for troubleshooting:
186
+ ```bash
187
+ etlplus list --config examples/configs/pipeline.yml --sources
188
+ etlplus list --config examples/configs/pipeline.yml --transforms
189
+ ```
190
+
191
+ #### Render SQL DDL
192
+
193
+ Use `etlplus render` to turn table schema specs into ready-to-run SQL. Render from a pipeline config
194
+ or from a standalone schema file, and choose the built-in `ddl` or `view` templates (or provide your
195
+ own).
196
+
197
+ Render all tables defined in a pipeline:
198
+ ```bash
199
+ etlplus render --config examples/configs/pipeline.yml --template ddl
200
+ ```
201
+
202
+ Render a single table in that pipeline:
203
+ ```bash
204
+ etlplus render --config examples/configs/pipeline.yml --table customers --template view
205
+ ```
206
+
207
+ Render from a standalone table spec to a file:
208
+ ```bash
209
+ etlplus render --spec schemas/customer.yml --template view -o temp/customer_view.sql
210
+ ```
211
+
212
+ #### Extract Data
213
+
214
+ Note: For file sources, the format is normally inferred from the filename extension. Use
215
+ `--source-format` to override inference when a file lacks an extension or when you want to force a
216
+ specific parser.
217
+
218
+ Extract from JSON file:
219
+ ```bash
220
+ etlplus extract file examples/data/sample.json
221
+ ```
222
+
223
+ Extract from CSV file:
224
+ ```bash
225
+ etlplus extract file examples/data/sample.csv
226
+ ```
227
+
228
+ Extract from XML file:
229
+ ```bash
230
+ etlplus extract file examples/data/sample.xml
231
+ ```
232
+
233
+ Extract from REST API:
234
+ ```bash
235
+ etlplus extract api https://api.example.com/data
236
+ ```
237
+
238
+ Save extracted data to file:
239
+ ```bash
240
+ etlplus extract file examples/data/sample.csv -o temp/sample_output.json
241
+ ```
242
+
243
+ #### Validate Data
244
+
245
+ Validate data from file or JSON string:
246
+ ```bash
247
+ etlplus validate '{"name": "John", "age": 30}' --rules '{"name": {"type": "string", "required": true}, "age": {"type": "number", "min": 0, "max": 150}}'
248
+ ```
249
+
250
+ Validate from file:
251
+ ```bash
252
+ etlplus validate examples/data/sample.json --rules '{"email": {"type": "string", "pattern": "^[\\w.-]+@[\\w.-]+\\.\\w+$"}}'
253
+ ```
254
+
255
+ #### Transform Data
256
+
257
+ When piping data through `etlplus transform`, use `--source-format` whenever the SOURCE argument is
258
+ `-` or a literal payload, mirroring the `etlplus extract` semantics. Use `--target-format` to
259
+ control the emitted format for stdout or other non-file outputs, just like `etlplus load`. File
260
+ paths continue to infer formats from their extensions. Use `--from` to override the inferred source
261
+ connector type and `--to` to override the inferred target connector type, matching the `etlplus
262
+ extract`/`etlplus load` behavior.
263
+
264
+ Transform file inputs while overriding connector types:
265
+ ```bash
266
+ etlplus transform --from file examples/data/sample.json \
267
+ --operations '{"select": ["name", "email"]}' \
268
+ --to file -o temp/selected_output.json
269
+ ```
270
+
271
+ Filter and select fields:
272
+ ```bash
273
+ etlplus transform '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]' \
274
+ --operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}'
275
+ ```
276
+
277
+ Sort data:
278
+ ```bash
279
+ etlplus transform examples/data/sample.json --operations '{"sort": {"field": "age", "reverse": true}}'
280
+ ```
281
+
282
+ Aggregate data:
283
+ ```bash
284
+ etlplus transform examples/data/sample.json --operations '{"aggregate": {"field": "age", "func": "sum"}}'
285
+ ```
286
+
287
+ Map/rename fields:
288
+ ```bash
289
+ etlplus transform examples/data/sample.json --operations '{"map": {"name": "new_name"}}'
290
+ ```
291
+
292
+ #### Load Data
293
+
294
+ `etlplus load` consumes JSON from stdin; provide only the target argument plus optional flags.
295
+
296
+ Load to JSON file:
297
+ ```bash
298
+ etlplus extract file examples/data/sample.json \
299
+ | etlplus load --to file temp/sample_output.json
300
+ ```
301
+
302
+ Load to CSV file:
303
+ ```bash
304
+ etlplus extract file examples/data/sample.csv \
305
+ | etlplus load --to file temp/sample_output.csv
306
+ ```
307
+
308
+ Load to REST API:
309
+ ```bash
310
+ cat examples/data/sample.json \
311
+ | etlplus load --to api https://api.example.com/endpoint
312
+ ```
313
+
314
+ ### Python API
315
+
316
+ Use ETLPlus as a Python library:
317
+
318
+ ```python
319
+ from etlplus import extract, validate, transform, load
320
+
321
+ # Extract data
322
+ data = extract("file", "data.json")
323
+
324
+ # Validate data
325
+ validation_rules = {
326
+ "name": {"type": "string", "required": True},
327
+ "age": {"type": "number", "min": 0, "max": 150}
328
+ }
329
+ result = validate(data, validation_rules)
330
+ if result["valid"]:
331
+ print("Data is valid!")
332
+
333
+ # Transform data
334
+ operations = {
335
+ "filter": {"field": "age", "op": "gt", "value": 18},
336
+ "select": ["name", "email"]
337
+ }
338
+ transformed = transform(data, operations)
339
+
340
+ # Load data
341
+ load(transformed, "file", "temp/sample_output.json", format="json")
342
+ ```
343
+
344
+ For YAML-driven pipelines executed end-to-end (extract → validate → transform → load), see:
345
+
346
+ - Authoring: [`docs/pipeline-guide.md`](docs/pipeline-guide.md)
347
+ - Runner API and internals: [`docs/run-module.md`](docs/run-module.md)
348
+
349
+ CLI quick reference for pipelines:
350
+
351
+ ```bash
352
+ # List jobs or show a pipeline summary
353
+ etlplus list --config examples/configs/pipeline.yml --jobs
354
+ etlplus list --config examples/configs/pipeline.yml --summary
355
+
356
+ # Run a job
357
+ etlplus run --config examples/configs/pipeline.yml --job file_to_file_customers
358
+
359
+ # Deprecated shim (will be removed): etlplus pipeline
360
+ ```
361
+
362
+ ### Complete ETL Pipeline Example
363
+
364
+ ```bash
365
+ # 1. Extract from CSV
366
+ etlplus extract file examples/data/sample.csv -o temp/sample_extracted.json
367
+
368
+ # 2. Transform (filter and select fields)
369
+ etlplus transform temp/sample_extracted.json \
370
+ --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
371
+ -o temp/sample_transformed.json
372
+
373
+ # 3. Validate transformed data
374
+ etlplus validate temp/sample_transformed.json \
375
+ --rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}'
376
+
377
+ # 4. Load to CSV
378
+ cat temp/sample_transformed.json \
379
+ | etlplus load --to temp/sample_output.csv
380
+ ```
381
+
382
+ ### Format Overrides
383
+
384
+ `--source-format` and `--target-format` override whichever format would normally be inferred from a
385
+ file extension. This is useful when an input lacks an extension (for example, `records.txt` that
386
+ actually contains CSV) or when you intentionally want to treat a file as another format.
387
+
388
+ Examples (zsh):
389
+
390
+ ```zsh
391
+ # Force CSV parsing for an extension-less file
392
+ etlplus extract --from file data.txt --source-format csv
393
+
394
+ # Write CSV to a file without the .csv suffix
395
+ etlplus load --to file output.bin --target-format csv < data.json
396
+
397
+ # Leave the flags off when extensions already match the desired format
398
+ etlplus extract --from file data.csv
399
+ etlplus load --to file data.json < data.json
400
+ ```
401
+
402
+ ## Transformation Operations
403
+
404
+ ### Filter Operations
405
+
406
+ Supported operators:
407
+ - `eq`: Equal
408
+ - `ne`: Not equal
409
+ - `gt`: Greater than
410
+ - `gte`: Greater than or equal
411
+ - `lt`: Less than
412
+ - `lte`: Less than or equal
413
+ - `in`: Value in list
414
+ - `contains`: List/string contains value
415
+
416
+ Example:
417
+ ```json
418
+ {
419
+ "filter": {
420
+ "field": "status",
421
+ "op": "in",
422
+ "value": ["active", "pending"]
423
+ }
424
+ }
425
+ ```
426
+
427
+ ### Aggregation Functions
428
+
429
+ Supported functions:
430
+ - `sum`: Sum of values
431
+ - `avg`: Average of values
432
+ - `min`: Minimum value
433
+ - `max`: Maximum value
434
+ - `count`: Count of values
435
+
436
+ Example:
437
+ ```json
438
+ {
439
+ "aggregate": {
440
+ "field": "revenue",
441
+ "func": "sum"
442
+ }
443
+ }
444
+ ```
445
+
446
+ ## Validation Rules
447
+
448
+ Supported validation rules:
449
+ - `type`: Data type (string, number, integer, boolean, array, object)
450
+ - `required`: Field is required (true/false)
451
+ - `min`: Minimum value for numbers
452
+ - `max`: Maximum value for numbers
453
+ - `minLength`: Minimum length for strings
454
+ - `maxLength`: Maximum length for strings
455
+ - `pattern`: Regex pattern for strings
456
+ - `enum`: List of allowed values
457
+
458
+ Example:
459
+ ```json
460
+ {
461
+ "email": {
462
+ "type": "string",
463
+ "required": true,
464
+ "pattern": "^[\\w.-]+@[\\w.-]+\\.\\w+$"
465
+ },
466
+ "age": {
467
+ "type": "number",
468
+ "min": 0,
469
+ "max": 150
470
+ },
471
+ "status": {
472
+ "type": "string",
473
+ "enum": ["active", "inactive", "pending"]
474
+ }
475
+ }
476
+ ```
477
+
478
+ ## Development
479
+
480
+ ### API Client Docs
481
+
482
+ Looking for the HTTP client and pagination helpers? See the dedicated docs in
483
+ `etlplus/api/README.md` for:
484
+
485
+ - Quickstart with `EndpointClient`
486
+ - Authentication via `EndpointCredentialsBearer`
487
+ - Pagination with `PaginationConfig` (page and cursor styles)
488
+ - Tips on `records_path` and `cursor_path`
489
+
490
+ ### Runner Internals and Connectors
491
+
492
+ Curious how the pipeline runner composes API requests, pagination, and load calls?
493
+
494
+ - Runner overview and helpers: [`docs/run-module.md`](docs/run-module.md)
495
+ - Unified "connector" vocabulary (API/File/DB): `etlplus/config/connector.py`
496
+ - API/file targets reuse the same shapes as sources; API targets typically set a `method`.
497
+
498
+ ### Running Tests
499
+
500
+ ```bash
501
+ pytest tests/ -v
502
+ ```
503
+
504
+ #### Test Layers
505
+
506
+ We split tests into two layers:
507
+
508
+ - **Unit (`tests/unit/`)**: single function or class, no real I/O, fast, uses stubs/monkeypatch
509
+ (e.g. `etlplus.cli.create_parser`, transform + validate helpers).
510
+ - **Integration (`tests/integration/`)**: end-to-end flows (CLI `main()`, pipeline `run()`,
511
+ pagination + rate limit defaults, file/API connector interactions) may touch temp files and use
512
+ fake clients.
513
+
514
+ If a test calls `etlplus.cli.main()` or `etlplus.run.run()` it’s integration by default. Full
515
+ criteria: [`CONTRIBUTING.md#testing`](CONTRIBUTING.md#testing).
516
+
517
+ ### Code Coverage
518
+
519
+ ```bash
520
+ pytest tests/ --cov=etlplus --cov-report=html
521
+ ```
522
+
523
+ ### Linting
524
+
525
+ ```bash
526
+ flake8 etlplus/
527
+ black etlplus/
528
+ ```
529
+
530
+ ### Updating Demo Snippets
531
+
532
+ `DEMO.md` shows the real output of `etlplus --version` captured from a freshly built wheel. Regenerate
533
+ the snippet (and the companion file [docs/snippets/installation_version.md](docs/snippets/installation_version.md)) after changing anything that affects the version string:
534
+
535
+ ```bash
536
+ make demo-snippets
537
+ ```
538
+
539
+ The helper script in [tools/update_demo_snippets.py](tools/update_demo_snippets.py) builds the wheel,
540
+ installs it into a throwaway virtual environment, runs `etlplus --version`, and rewrites the snippet
541
+ between the markers in [DEMO.md](DEMO.md).
542
+
543
+ ### Releasing to PyPI
544
+
545
+ `setuptools-scm` derives the package version from Git tags, so publishing is now entirely tag
546
+ driven—no hand-editing `pyproject.toml`, `setup.py`, or `etlplus/__version__.py`.
547
+
548
+ 1. Ensure `main` is green and the changelog/docs are up to date.
549
+ 2. Create and push a SemVer tag matching the `v*.*.*` pattern:
550
+
551
+ ```bash
552
+ git tag -a v1.4.0 -m "Release v1.4.0"
553
+ git push origin v1.4.0
554
+ ```
555
+
556
+ 3. GitHub Actions fetches tags, builds the sdist/wheel, and publishes to PyPI via the `publish` job
557
+ in [.github/workflows/ci.yml](.github/workflows/ci.yml).
558
+
559
+ If you want an extra smoke-test before tagging, run `make dist && pip install dist/*.whl` locally;
560
+ this exercises the same build path the workflow uses.
561
+
562
+ ## Links
563
+
564
+ - API client docs: [`etlplus/api/README.md`](etlplus/api/README.md)
565
+ - Examples: [`examples/README.md`](examples/README.md)
566
+ - Pipeline authoring guide: [`docs/pipeline-guide.md`](docs/pipeline-guide.md)
567
+ - Runner internals: [`docs/run-module.md`](docs/run-module.md)
568
+ - Design notes (Mapping inputs, dict outputs): [`docs/pipeline-guide.md#design-notes-mapping-inputs-dict-outputs`](docs/pipeline-guide.md#design-notes-mapping-inputs-dict-outputs)
569
+ - Typing philosophy: [`CONTRIBUTING.md#typing-philosophy`](CONTRIBUTING.md#typing-philosophy)
570
+ - Demo and walkthrough: [`DEMO.md`](DEMO.md)
571
+ - Additional references: [`REFERENCES.md`](`REFERENCES.md)
572
+
573
+ ## License
574
+
575
+ This project is licensed under the [MIT License](LICENSE).
576
+
577
+ ## Contributing
578
+
579
+ Code and codeless contributions are welcome! If you’d like to add a new feature, fix a bug, or
580
+ improve the documentation, please feel free to submit a pull request as follows:
581
+
582
+ 1. Fork this repository.
583
+ 2. Create a new feature branch for your changes (`git checkout -b feature/feature-name`).
584
+ 3. Commit your changes (`git commit -m "Add feature"`).
585
+ 4. Push to your branch (`git push origin feature-name`).
586
+ 5. Submit a pull request with a detailed description.
587
+
588
+ If you choose to be a code contributor, please first refer these documents:
589
+
590
+ - Pipeline authoring guide: [`docs/pipeline-guide.md`](docs/pipeline-guide.md)
591
+ - Design notes (Mapping inputs, dict outputs):
592
+ [`docs/pipeline-guide.md#design-notes-mapping-inputs-dict-outputs`](docs/pipeline-guide.md#design-notes-mapping-inputs-dict-outputs)
593
+ - Typing philosophy (TypedDicts as editor hints, permissive runtime):
594
+ [`CONTRIBUTING.md#typing-philosophy`](CONTRIBUTING.md#typing-philosophy)
595
+
596
+ ## Acknowledgments
597
+
598
+ ETLPlus is inspired by common work patterns in data engineering and software engineering patterns in
599
+ Python development, aiming to increase productivity and reduce boilerplate code. Feedback and
600
+ contributions are always appreciated!
601
+
602
+ [Codecov project]: https://codecov.io/github/Dagitali/ETLPlus?branch=main
603
+ [GitHub Actions CI workflow]: https://github.com/Dagitali/ETLPlus/actions/workflows/ci.yml
604
+ [GitHub contributors]: https://github.com/Dagitali/ETLPlus/graphs/contributors
605
+ [GitHub issues]: https://github.com/Dagitali/ETLPlus/issues
606
+ [GitHub PRs]: https://github.com/Dagitali/ETLPlus/pulls
607
+ [GitHub release]: https://github.com/Dagitali/ETLPlus/releases
608
+ [PyPI package]: https://pypi.org/project/etlplus/
@@ -0,0 +1,55 @@
1
+ etlplus/__init__.py,sha256=M2gScnyir6WOMAh_EuoQIiAzdcTls0_5hbd_Q6of8I0,1021
2
+ etlplus/__main__.py,sha256=btoROneNiigyfBU7BSzPKZ1R9gzBMpxcpsbPwmuHwTM,479
3
+ etlplus/__version__.py,sha256=1E0GMK_yUWCMQFKxXjTvyMwofi0qT2k4CDNiHWiymWE,327
4
+ etlplus/ddl.py,sha256=uYkiMTx1uDlUypnXCYy0K5ARnHRMHFVzzg8PizBQRLg,5306
5
+ etlplus/enums.py,sha256=V_j18Ud2BCXpFsBk2pZGrvCVrvAMJ7uja1z9fppFGso,10175
6
+ etlplus/extract.py,sha256=f44JdHhNTACxgn44USx05paKTwq7LQY-V4wANCW9hVM,6173
7
+ etlplus/file.py,sha256=RxIAsGDN4f_vNA2B5-ct88JNd_ISAyYbooIRE5DstS8,17972
8
+ etlplus/load.py,sha256=BwF3gT4gIr-5CvNMz_aLTCl-w2ihWSTxNVd4X92XFwI,8737
9
+ etlplus/mixins.py,sha256=ifGpHwWv7U00yqGf-kN93vJax2IiK4jaGtTsPsO3Oak,1350
10
+ etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ etlplus/run.py,sha256=zl_Yx35spcgaa9Xx7-kcJEb1CAYyMIiqtPlsSrYxRfs,12448
12
+ etlplus/run_helpers.py,sha256=bj6MkaeFxjl3CeKG1HoXKx5DwAlXNERVW-GX-z1P_qQ,24373
13
+ etlplus/transform.py,sha256=uAUVDDHYCgx7GpVez9IK3OAZM-CnCuMa9iox3vwGGJA,25296
14
+ etlplus/types.py,sha256=SJiZ7wJiSnV4CEvF-9E5nSFLBo4DT9OqHQqj1GSHkv8,6042
15
+ etlplus/utils.py,sha256=_fn8b-SAdxiw28VX-Ugr8sZUPZI9mEkWKAGExlgxhJA,13993
16
+ etlplus/validate.py,sha256=7rJoEI_SIILdPpoBqqh2UJqg9oeReDz34mYSlc3t7Qg,12989
17
+ etlplus/api/README.md,sha256=UkK5PiZWXbbnMNP0MaPa56S88PjSqOwhMNCyswOhvKc,7329
18
+ etlplus/api/__init__.py,sha256=P2JUYFy6Ep4t6xnsBiCBfQCkQLHYYhA-yXPXCobS8Y0,4295
19
+ etlplus/api/auth.py,sha256=GOO5on-LoMS1GXTAhtK9rFcfpjbBcNeA6NE5UZwIq0g,12158
20
+ etlplus/api/config.py,sha256=wRpOaZ31sPReVzEMme0jKl_37nqgraESwuYSNxP_xDo,17397
21
+ etlplus/api/endpoint_client.py,sha256=PxCvBsvFhTIjEbY6drIIvciynHXQEvKu47Pi63Gxwqs,30693
22
+ etlplus/api/errors.py,sha256=XjI2xW-sypMUNUbqfc2S57-IGyWnH3oCDFhCmKYYI_Q,4648
23
+ etlplus/api/request_manager.py,sha256=YkDz803HM3BBzamsEZdSdE9fbVT0avMbTaLAgar9Wzo,18481
24
+ etlplus/api/retry_manager.py,sha256=0GDhJVyIlb1Ww35JUWlYoa8QYUPjKLBtxQeZj3TdLbY,11306
25
+ etlplus/api/transport.py,sha256=LRsQEPxIYrvXQQMvgPPkIl_57YCmanzsWNEnSYdP_d8,9164
26
+ etlplus/api/types.py,sha256=687JigIf3qfYxgGTNBaMNsQsrza5Pja6DcK5llM9oRU,4591
27
+ etlplus/api/pagination/__init__.py,sha256=a4UX2J0AG8RMvmHt_CCofUm5vSmFo6GAfkb8XnSXypM,1395
28
+ etlplus/api/pagination/client.py,sha256=42cG442od3mQkw_JsvGvxT_w7y9J4HPM5PB4tFFU6EQ,5383
29
+ etlplus/api/pagination/config.py,sha256=3dXDJ-nMbO9Zk6i344n4roBFbUlHsa294D1_plPmm6E,13579
30
+ etlplus/api/pagination/paginator.py,sha256=wtdY_er4yfjx5yTUQJ1gPq-IuWmpLAHeG5buBQZJm54,24453
31
+ etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-Y_lbU6kI,1070
32
+ etlplus/api/rate_limiting/config.py,sha256=2b4wIynblN-1EyMqI4aXa71SljzSjXYh5N1Nngr3jOg,9406
33
+ etlplus/api/rate_limiting/rate_limiter.py,sha256=Uxozqd_Ej5Lsj-M-mLT2WexChgWh7x35_YP10yqYPQA,7159
34
+ etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
35
+ etlplus/cli/app.py,sha256=buGIIoSIu5cxbYTdPcA_iaxJaPG-eHj-LPD9OgZ0h9w,35824
36
+ etlplus/cli/handlers.py,sha256=O7Mh9nowdMCzaV36KASWZVC4fNMEg9xnVZXE7NHW6P8,18873
37
+ etlplus/cli/main.py,sha256=5qWAKqlRtnb4VEpBfGT45q-LBxi_2hSMnw23jNyYA_Q,16497
38
+ etlplus/config/__init__.py,sha256=VZWzOg7d2YR9NT6UwKTv44yf2FRUMjTHynkm1Dl5Qzo,1486
39
+ etlplus/config/connector.py,sha256=0-TIwevHbKRHVmucvyGpPd-3tB1dKHB-dj0yJ6kq5eY,9809
40
+ etlplus/config/jobs.py,sha256=hmzRCqt0OvCEZZR4ONKrd3lvSv0OmayjLc4yOBk3ug8,7399
41
+ etlplus/config/pipeline.py,sha256=Va4MQY6KEyKqHGMKPmh09ZcGpx95br-iNUjpkqtzVbw,9500
42
+ etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,1898
43
+ etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
44
+ etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
45
+ etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
46
+ etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
47
+ etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
48
+ etlplus/validation/__init__.py,sha256=Pe5Xg1_EA4uiNZGYu5WTF3j7odjmyxnAJ8rcioaplSQ,1254
49
+ etlplus/validation/utils.py,sha256=Mtqg449VIke0ziy_wd2r6yrwJzQkA1iulZC87FzXMjo,10201
50
+ etlplus-0.5.2.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
51
+ etlplus-0.5.2.dist-info/METADATA,sha256=ow6T-Op0DnqalPB2eMLgaJ0s-a3WQUJ4wBQs_HxrQ9k,18936
52
+ etlplus-0.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
53
+ etlplus-0.5.2.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
54
+ etlplus-0.5.2.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
55
+ etlplus-0.5.2.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ etlplus = etlplus.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dagitali LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ etlplus