etlplus 0.8.6__tar.gz → 0.10.1__tar.gz

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 (150) hide show
  1. {etlplus-0.8.6 → etlplus-0.10.1}/DEMO.md +25 -23
  2. {etlplus-0.8.6/etlplus.egg-info → etlplus-0.10.1}/PKG-INFO +61 -29
  3. {etlplus-0.8.6 → etlplus-0.10.1}/README.md +60 -28
  4. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/commands.py +175 -121
  5. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/constants.py +13 -7
  6. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/handlers.py +3 -4
  7. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/io.py +21 -5
  8. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/main.py +2 -1
  9. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/options.py +1 -1
  10. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/state.py +3 -2
  11. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/enums.py +161 -1
  12. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/file.py +11 -16
  13. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/load.py +1 -1
  14. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/utils.py +1 -1
  15. {etlplus-0.8.6 → etlplus-0.10.1/etlplus.egg-info}/PKG-INFO +61 -29
  16. {etlplus-0.8.6 → etlplus-0.10.1}/examples/README.md +4 -3
  17. {etlplus-0.8.6 → etlplus-0.10.1}/examples/quickstart_python.py +1 -1
  18. {etlplus-0.8.6 → etlplus-0.10.1}/tests/conftest.py +2 -2
  19. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_cli.py +127 -0
  20. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_pagination_strategy.py +2 -2
  21. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/cli/test_u_cli_handlers.py +27 -13
  22. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/cli/test_u_cli_main.py +3 -3
  23. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_enums.py +31 -0
  24. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_file.py +50 -1
  25. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_load.py +1 -1
  26. {etlplus-0.8.6 → etlplus-0.10.1}/.coveragerc +0 -0
  27. {etlplus-0.8.6 → etlplus-0.10.1}/.editorconfig +0 -0
  28. {etlplus-0.8.6 → etlplus-0.10.1}/.gitattributes +0 -0
  29. {etlplus-0.8.6 → etlplus-0.10.1}/.github/actions/python-bootstrap/action.yml +0 -0
  30. {etlplus-0.8.6 → etlplus-0.10.1}/.github/workflows/ci.yml +0 -0
  31. {etlplus-0.8.6 → etlplus-0.10.1}/.gitignore +0 -0
  32. {etlplus-0.8.6 → etlplus-0.10.1}/.pre-commit-config.yaml +0 -0
  33. {etlplus-0.8.6 → etlplus-0.10.1}/.ruff.toml +0 -0
  34. {etlplus-0.8.6 → etlplus-0.10.1}/CODE_OF_CONDUCT.md +0 -0
  35. {etlplus-0.8.6 → etlplus-0.10.1}/CONTRIBUTING.md +0 -0
  36. {etlplus-0.8.6 → etlplus-0.10.1}/LICENSE +0 -0
  37. {etlplus-0.8.6 → etlplus-0.10.1}/MANIFEST.in +0 -0
  38. {etlplus-0.8.6 → etlplus-0.10.1}/Makefile +0 -0
  39. {etlplus-0.8.6 → etlplus-0.10.1}/REFERENCES.md +0 -0
  40. {etlplus-0.8.6 → etlplus-0.10.1}/docs/README.md +0 -0
  41. {etlplus-0.8.6 → etlplus-0.10.1}/docs/pipeline-guide.md +0 -0
  42. {etlplus-0.8.6 → etlplus-0.10.1}/docs/snippets/installation_version.md +0 -0
  43. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/__init__.py +0 -0
  44. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/__main__.py +0 -0
  45. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/__version__.py +0 -0
  46. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/README.md +0 -0
  47. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/__init__.py +0 -0
  48. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/auth.py +0 -0
  49. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/config.py +0 -0
  50. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/endpoint_client.py +0 -0
  51. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/errors.py +0 -0
  52. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/pagination/__init__.py +0 -0
  53. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/pagination/client.py +0 -0
  54. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/pagination/config.py +0 -0
  55. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/pagination/paginator.py +0 -0
  56. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/rate_limiting/__init__.py +0 -0
  57. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/rate_limiting/config.py +0 -0
  58. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/rate_limiting/rate_limiter.py +0 -0
  59. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/request_manager.py +0 -0
  60. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/retry_manager.py +0 -0
  61. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/transport.py +0 -0
  62. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/api/types.py +0 -0
  63. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/__init__.py +0 -0
  64. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/cli/types.py +0 -0
  65. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/config/__init__.py +0 -0
  66. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/config/connector.py +0 -0
  67. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/config/jobs.py +0 -0
  68. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/config/pipeline.py +0 -0
  69. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/config/profile.py +0 -0
  70. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/config/types.py +0 -0
  71. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/config/utils.py +0 -0
  72. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/database/__init__.py +0 -0
  73. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/database/ddl.py +0 -0
  74. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/database/engine.py +0 -0
  75. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/database/orm.py +0 -0
  76. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/database/schema.py +0 -0
  77. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/database/types.py +0 -0
  78. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/extract.py +0 -0
  79. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/mixins.py +0 -0
  80. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/py.typed +0 -0
  81. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/run.py +0 -0
  82. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/run_helpers.py +0 -0
  83. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/templates/__init__.py +0 -0
  84. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/templates/ddl.sql.j2 +0 -0
  85. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/templates/view.sql.j2 +0 -0
  86. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/transform.py +0 -0
  87. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/types.py +0 -0
  88. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/validate.py +0 -0
  89. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/validation/__init__.py +0 -0
  90. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus/validation/utils.py +0 -0
  91. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus.egg-info/SOURCES.txt +0 -0
  92. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus.egg-info/dependency_links.txt +0 -0
  93. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus.egg-info/entry_points.txt +0 -0
  94. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus.egg-info/requires.txt +0 -0
  95. {etlplus-0.8.6 → etlplus-0.10.1}/etlplus.egg-info/top_level.txt +0 -0
  96. {etlplus-0.8.6 → etlplus-0.10.1}/examples/configs/ddl_spec.yml +0 -0
  97. {etlplus-0.8.6 → etlplus-0.10.1}/examples/configs/pipeline.yml +0 -0
  98. {etlplus-0.8.6 → etlplus-0.10.1}/examples/data/sample.csv +0 -0
  99. {etlplus-0.8.6 → etlplus-0.10.1}/examples/data/sample.json +0 -0
  100. {etlplus-0.8.6 → etlplus-0.10.1}/examples/data/sample.xml +0 -0
  101. {etlplus-0.8.6 → etlplus-0.10.1}/examples/data/sample.xsd +0 -0
  102. {etlplus-0.8.6 → etlplus-0.10.1}/examples/data/sample.yaml +0 -0
  103. {etlplus-0.8.6 → etlplus-0.10.1}/pyproject.toml +0 -0
  104. {etlplus-0.8.6 → etlplus-0.10.1}/pytest.ini +0 -0
  105. {etlplus-0.8.6 → etlplus-0.10.1}/setup.cfg +0 -0
  106. {etlplus-0.8.6 → etlplus-0.10.1}/setup.py +0 -0
  107. {etlplus-0.8.6 → etlplus-0.10.1}/tests/__init__.py +0 -0
  108. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/conftest.py +0 -0
  109. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_examples_data_parity.py +0 -0
  110. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_pipeline_smoke.py +0 -0
  111. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_pipeline_yaml_load.py +0 -0
  112. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_run.py +0 -0
  113. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_run_profile_pagination_defaults.py +0 -0
  114. {etlplus-0.8.6 → etlplus-0.10.1}/tests/integration/test_i_run_profile_rate_limit_defaults.py +0 -0
  115. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/conftest.py +0 -0
  116. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_auth.py +0 -0
  117. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_config.py +0 -0
  118. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_endpoint_client.py +0 -0
  119. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_mocks.py +0 -0
  120. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_pagination_client.py +0 -0
  121. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_pagination_config.py +0 -0
  122. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_paginator.py +0 -0
  123. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_rate_limit_config.py +0 -0
  124. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_rate_limiter.py +0 -0
  125. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_request_manager.py +0 -0
  126. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_retry_manager.py +0 -0
  127. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_transport.py +0 -0
  128. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/api/test_u_types.py +0 -0
  129. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/cli/conftest.py +0 -0
  130. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/cli/test_u_cli_state.py +0 -0
  131. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/config/test_u_config_utils.py +0 -0
  132. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/config/test_u_connector.py +0 -0
  133. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/config/test_u_jobs.py +0 -0
  134. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/config/test_u_pipeline.py +0 -0
  135. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/conftest.py +0 -0
  136. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/database/test_u_database_ddl.py +0 -0
  137. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/database/test_u_database_engine.py +0 -0
  138. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/database/test_u_database_orm.py +0 -0
  139. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/database/test_u_database_schema.py +0 -0
  140. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_extract.py +0 -0
  141. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_main.py +0 -0
  142. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_mixins.py +0 -0
  143. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_run.py +0 -0
  144. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_run_helpers.py +0 -0
  145. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_transform.py +0 -0
  146. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_utils.py +0 -0
  147. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_validate.py +0 -0
  148. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/test_u_version.py +0 -0
  149. {etlplus-0.8.6 → etlplus-0.10.1}/tests/unit/validation/test_u_validation_utils.py +0 -0
  150. {etlplus-0.8.6 → etlplus-0.10.1}/tools/update_demo_snippets.py +0 -0
@@ -89,14 +89,14 @@ $ etlplus validate '{"email": "user@example.com", "age": 25}' \
89
89
 
90
90
  ### Filter and Select
91
91
  ```bash
92
- $ etlplus transform '[
92
+ $ etlplus transform --operations '{
93
+ "filter": {"field": "age", "op": "gt", "value": 26},
94
+ "select": ["name", "age"]
95
+ }' '[
93
96
  {"name": "John", "age": 30, "city": "NYC"},
94
97
  {"name": "Jane", "age": 25, "city": "LA"},
95
98
  {"name": "Bob", "age": 35, "city": "Chicago"}
96
- ]' --operations '{
97
- "filter": {"field": "age", "op": "gt", "value": 26},
98
- "select": ["name", "age"]
99
- }'
99
+ ]'
100
100
  [
101
101
  {
102
102
  "name": "John",
@@ -111,24 +111,19 @@ $ etlplus transform '[
111
111
 
112
112
  ### Sort Data
113
113
  ```bash
114
- $ etlplus transform '[
115
- {"name": "Charlie", "score": 85},
116
- {"name": "Alice", "score": 95},
117
- {"name": "Bob", "score": 90}
118
- ]' --operations '{
119
- "sort": {"field": "score", "reverse": true}
120
- }'
114
+ $ etlplus transform -\
115
+ -operations '{"sort": {"field": "score", "reverse": true}}' \
116
+ '[{"name": "Charlie", "score": 85}, {"name": "Alice", "score": 95}, {"name": "Bob", "score": 90}]'
121
117
  ```
122
118
 
123
119
  ### Aggregate Data
124
120
  ```bash
125
- $ etlplus transform '[
121
+ $ etlplus transform --operations '{"aggregate": {"field": "sales", "func": "sum"}}' \
122
+ '[
126
123
  {"product": "A", "sales": 100},
127
124
  {"product": "B", "sales": 150},
128
125
  {"product": "C", "sales": 200}
129
- ]' --operations '{
130
- "aggregate": {"field": "sales", "func": "sum"}
131
- }'
126
+ ]'
132
127
  {
133
128
  "sum_sales": 450
134
129
  }
@@ -138,7 +133,9 @@ $ etlplus transform '[
138
133
 
139
134
  ### Load to JSON File
140
135
  ```bash
141
- $ etlplus load '{"name": "John", "status": "active"}' file output.json
136
+ $ etlplus load \
137
+ '{"name": "John", "status": "active"}' \
138
+ output.json --target-type file
142
139
  {
143
140
  "status": "success",
144
141
  "message": "Data loaded to output.json",
@@ -148,10 +145,12 @@ $ etlplus load '{"name": "John", "status": "active"}' file output.json
148
145
 
149
146
  ### Load to CSV File
150
147
  ```bash
151
- $ etlplus load '[
148
+ $ etlplus load \
149
+ '[
152
150
  {"name": "John", "email": "john@example.com"},
153
151
  {"name": "Jane", "email": "jane@example.com"}
154
- ]' --to users.csv
152
+ ]' \
153
+ users.csv --target-type file
155
154
  {
156
155
  "status": "success",
157
156
  "message": "Data loaded to users.csv",
@@ -173,19 +172,22 @@ This example shows a complete ETL workflow:
173
172
  $ etlplus extract raw_data.csv > extracted.json
174
173
 
175
174
  # Step 2: Transform
176
- $ etlplus transform --from extracted.json \
175
+ $ etlplus transform \
177
176
  --operations '{
178
177
  "filter": {"field": "age", "op": "gte", "value": 18},
179
178
  "select": ["name", "email", "age"]
180
- }' --to transformed.json
179
+ }' \
180
+ extracted.json \
181
+ transformed.json
181
182
 
182
183
  # Step 3: Validate
183
- $ etlplus validate transformed.json \
184
+ $ etlplus validate \
184
185
  --rules '{
185
186
  "name": {"type": "string", "required": true},
186
187
  "email": {"type": "string", "required": true, "pattern": "^[\\w.-]+@[\\w.-]+\\.\\w+$"},
187
188
  "age": {"type": "number", "min": 18, "max": 120}
188
- }'
189
+ }' \
190
+ transformed.json
189
191
 
190
192
  # Step 4: Load
191
193
  $ etlplus load transformed.json file final_output.csv
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.8.6
3
+ Version: 0.10.1
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
@@ -64,6 +64,7 @@ package and command-line interface for data extraction, validation, transformati
64
64
  - [Quickstart](#quickstart)
65
65
  - [Usage](#usage)
66
66
  - [Command Line Interface](#command-line-interface)
67
+ - [Argument Order and Required Options](#argument-order-and-required-options)
67
68
  - [Check Pipelines](#check-pipelines)
68
69
  - [Render SQL DDL](#render-sql-ddl)
69
70
  - [Extract Data](#extract-data)
@@ -151,8 +152,8 @@ etlplus --version
151
152
 
152
153
  # One-liner: extract CSV, filter, select, and write JSON
153
154
  etlplus extract file examples/data/sample.csv \
154
- | etlplus transform - --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
155
- -o temp/sample_output.json
155
+ | etlplus transform --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
156
+ - temp/sample_output.json
156
157
  ```
157
158
 
158
159
  [Python API](#python-api):
@@ -185,6 +186,27 @@ etlplus --version
185
186
  The CLI is implemented with Typer (Click-based). There is no argparse compatibility layer, so rely
186
187
  on the documented commands/flags and run `etlplus <command> --help` for current options.
187
188
 
189
+ **Example error messages:**
190
+
191
+ - If you omit a required argument: `Error: Missing required argument 'SOURCE'.`
192
+ - If you place an option before its argument: `Error: Option '--source-format' must follow the 'SOURCE' argument.`
193
+
194
+ #### Argument Order and Required Options
195
+
196
+ For each command, positional arguments must precede options. Required options must follow their
197
+ associated argument:
198
+
199
+ - **extract**: `etlplus extract SOURCE [--source-format ...] [--source-type ...]`
200
+ - `SOURCE` is required. `--source-format` and `--source-type` must follow `SOURCE`.
201
+ - **transform**: `etlplus transform [--operations ...] SOURCE [--source-format ...] [--source-type ...] TARGET [--target-format ...] [--target-type ...]`
202
+ - `SOURCE` and `TARGET` are required. Format/type options must follow their respective argument.
203
+ - **load**: `etlplus load TARGET [--target-format ...] [--target-type ...] [--source-format ...]`
204
+ - `TARGET` is required. `--target-format` and `--target-type` must follow `TARGET`.
205
+ - **validate**: `etlplus validate SOURCE [--rules ...] [--source-format ...] [--source-type ...]`
206
+ - `SOURCE` is required. `--rules` and format/type options must follow `SOURCE`.
207
+
208
+ If required arguments or options are missing, or if options are placed before their associated argument, the CLI will display a clear error message.
209
+
188
210
  #### Check Pipelines
189
211
 
190
212
  Use `etlplus check` to explore pipeline YAML definitions without running them. The command can print
@@ -251,7 +273,7 @@ etlplus extract api https://api.example.com/data
251
273
 
252
274
  Save extracted data to file:
253
275
  ```bash
254
- etlplus extract file examples/data/sample.csv -o temp/sample_output.json
276
+ etlplus extract file examples/data/sample.csv > temp/sample_output.json
255
277
  ```
256
278
 
257
279
  #### Validate Data
@@ -270,59 +292,67 @@ etlplus validate examples/data/sample.json --rules '{"email": {"type": "string",
270
292
 
271
293
  When piping data through `etlplus transform`, use `--source-format` whenever the SOURCE argument is
272
294
  `-` or a literal payload, mirroring the `etlplus extract` semantics. Use `--target-format` to
273
- control the emitted format for stdout or other non-file outputs, just like `etlplus load`. File
274
- paths continue to infer formats from their extensions. Use `--from` to override the inferred source
275
- connector type and `--to` to override the inferred target connector type, matching the `etlplus
276
- extract`/`etlplus load` behavior.
295
+ control the emitted format for STDOUT or other non-file outputs, just like `etlplus load`. File
296
+ paths continue to infer formats from their extensions. Use `--source-type` to override the inferred
297
+ source connector type and `--target-type` to override the inferred target connector type, matching
298
+ the `etlplus extract`/`etlplus load` behavior.
277
299
 
278
300
  Transform file inputs while overriding connector types:
279
301
  ```bash
280
- etlplus transform --from file examples/data/sample.json \
302
+ etlplus transform \
281
303
  --operations '{"select": ["name", "email"]}' \
282
- --to file -o temp/selected_output.json
304
+ examples/data/sample.json --source-type file \
305
+ temp/selected_output.json --target-type file
283
306
  ```
284
307
 
285
308
  Filter and select fields:
286
309
  ```bash
287
- etlplus transform '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]' \
288
- --operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}'
310
+ etlplus transform \
311
+ --operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}' \
312
+ '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]'
289
313
  ```
290
314
 
291
315
  Sort data:
292
316
  ```bash
293
- etlplus transform examples/data/sample.json --operations '{"sort": {"field": "age", "reverse": true}}'
317
+ etlplus transform \
318
+ --operations '{"sort": {"field": "age", "reverse": true}}' \
319
+ examples/data/sample.json
294
320
  ```
295
321
 
296
322
  Aggregate data:
297
323
  ```bash
298
- etlplus transform examples/data/sample.json --operations '{"aggregate": {"field": "age", "func": "sum"}}'
324
+ etlplus transform \
325
+ --operations '{"aggregate": {"field": "age", "func": "sum"}}' \
326
+ examples/data/sample.json
299
327
  ```
300
328
 
301
329
  Map/rename fields:
302
330
  ```bash
303
- etlplus transform examples/data/sample.json --operations '{"map": {"name": "new_name"}}'
331
+ etlplus transform \
332
+ --operations '{"map": {"name": "new_name"}}' \
333
+ examples/data/sample.json
304
334
  ```
305
335
 
306
336
  #### Load Data
307
337
 
308
- `etlplus load` consumes JSON from stdin; provide only the target argument plus optional flags.
338
+ `etlplus load` consumes JSON from STDIN; provide only the target argument plus optional flags.
309
339
 
310
340
  Load to JSON file:
311
341
  ```bash
312
342
  etlplus extract file examples/data/sample.json \
313
- | etlplus load --to file temp/sample_output.json
343
+ | etlplus load temp/sample_output.json --target-type file
314
344
  ```
315
345
 
316
346
  Load to CSV file:
317
347
  ```bash
318
348
  etlplus extract file examples/data/sample.csv \
319
- | etlplus load --to file temp/sample_output.csv
349
+ | etlplus load temp/sample_output.csv --target-type file
320
350
  ```
321
351
 
322
352
  Load to REST API:
323
353
  ```bash
324
354
  cat examples/data/sample.json \
325
- | etlplus load --to api https://api.example.com/endpoint
355
+ | etlplus load https://api.example.com/endpoint --target-type api
326
356
  ```
327
357
 
328
358
  ### Python API
@@ -375,20 +405,22 @@ etlplus run --config examples/configs/pipeline.yml --job file_to_file_customers
375
405
 
376
406
  ```bash
377
407
  # 1. Extract from CSV
378
- etlplus extract file examples/data/sample.csv -o temp/sample_extracted.json
408
+ etlplus extract file examples/data/sample.csv > temp/sample_extracted.json
379
409
 
380
410
  # 2. Transform (filter and select fields)
381
- etlplus transform temp/sample_extracted.json \
411
+ etlplus transform \
382
412
  --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
383
- -o temp/sample_transformed.json
413
+ temp/sample_extracted.json \
414
+ temp/sample_transformed.json
384
415
 
385
416
  # 3. Validate transformed data
386
- etlplus validate temp/sample_transformed.json \
387
- --rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}'
417
+ etlplus validate \
418
+ --rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}' \
419
+ temo/sample_transformed.json
388
420
 
389
421
  # 4. Load to CSV
390
422
  cat temp/sample_transformed.json \
391
- | etlplus load --to temp/sample_output.csv
423
+ | etlplus load temp/sample_output.csv
392
424
  ```
393
425
 
394
426
  ### Format Overrides
@@ -401,14 +433,14 @@ Examples (zsh):
401
433
 
402
434
  ```zsh
403
435
  # Force CSV parsing for an extension-less file
404
- etlplus extract --from file data.txt --source-format csv
436
+ etlplus extract data.txt --source-type file --source-format csv
405
437
 
406
438
  # Write CSV to a file without the .csv suffix
407
- etlplus load --to file output.bin --target-format csv < data.json
439
+ etlplus load output.bin --target-type file --target-format csv < data.json
408
440
 
409
441
  # Leave the flags off when extensions already match the desired format
410
- etlplus extract --from file data.csv
411
- etlplus load --to file data.json < data.json
442
+ etlplus extract data.csv --source-type file
443
+ etlplus load data.json --target-type file < data.json
412
444
  ```
413
445
 
414
446
  ## Transformation Operations
@@ -19,6 +19,7 @@ package and command-line interface for data extraction, validation, transformati
19
19
  - [Quickstart](#quickstart)
20
20
  - [Usage](#usage)
21
21
  - [Command Line Interface](#command-line-interface)
22
+ - [Argument Order and Required Options](#argument-order-and-required-options)
22
23
  - [Check Pipelines](#check-pipelines)
23
24
  - [Render SQL DDL](#render-sql-ddl)
24
25
  - [Extract Data](#extract-data)
@@ -106,8 +107,8 @@ etlplus --version
106
107
 
107
108
  # One-liner: extract CSV, filter, select, and write JSON
108
109
  etlplus extract file examples/data/sample.csv \
109
- | etlplus transform - --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
110
- -o temp/sample_output.json
110
+ | etlplus transform --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
111
+ - temp/sample_output.json
111
112
  ```
112
113
 
113
114
  [Python API](#python-api):
@@ -140,6 +141,27 @@ etlplus --version
140
141
  The CLI is implemented with Typer (Click-based). There is no argparse compatibility layer, so rely
141
142
  on the documented commands/flags and run `etlplus <command> --help` for current options.
142
143
 
144
+ **Example error messages:**
145
+
146
+ - If you omit a required argument: `Error: Missing required argument 'SOURCE'.`
147
+ - If you place an option before its argument: `Error: Option '--source-format' must follow the 'SOURCE' argument.`
148
+
149
+ #### Argument Order and Required Options
150
+
151
+ For each command, positional arguments must precede options. Required options must follow their
152
+ associated argument:
153
+
154
+ - **extract**: `etlplus extract SOURCE [--source-format ...] [--source-type ...]`
155
+ - `SOURCE` is required. `--source-format` and `--source-type` must follow `SOURCE`.
156
+ - **transform**: `etlplus transform [--operations ...] SOURCE [--source-format ...] [--source-type ...] TARGET [--target-format ...] [--target-type ...]`
157
+ - `SOURCE` and `TARGET` are required. Format/type options must follow their respective argument.
158
+ - **load**: `etlplus load TARGET [--target-format ...] [--target-type ...] [--source-format ...]`
159
+ - `TARGET` is required. `--target-format` and `--target-type` must follow `TARGET`.
160
+ - **validate**: `etlplus validate SOURCE [--rules ...] [--source-format ...] [--source-type ...]`
161
+ - `SOURCE` is required. `--rules` and format/type options must follow `SOURCE`.
162
+
163
+ If required arguments or options are missing, or if options are placed before their associated argument, the CLI will display a clear error message.
164
+
143
165
  #### Check Pipelines
144
166
 
145
167
  Use `etlplus check` to explore pipeline YAML definitions without running them. The command can print
@@ -206,7 +228,7 @@ etlplus extract api https://api.example.com/data
206
228
 
207
229
  Save extracted data to file:
208
230
  ```bash
209
- etlplus extract file examples/data/sample.csv -o temp/sample_output.json
231
+ etlplus extract file examples/data/sample.csv > temp/sample_output.json
210
232
  ```
211
233
 
212
234
  #### Validate Data
@@ -225,59 +247,67 @@ etlplus validate examples/data/sample.json --rules '{"email": {"type": "string",
225
247
 
226
248
  When piping data through `etlplus transform`, use `--source-format` whenever the SOURCE argument is
227
249
  `-` or a literal payload, mirroring the `etlplus extract` semantics. Use `--target-format` to
228
- control the emitted format for stdout or other non-file outputs, just like `etlplus load`. File
229
- paths continue to infer formats from their extensions. Use `--from` to override the inferred source
230
- connector type and `--to` to override the inferred target connector type, matching the `etlplus
231
- extract`/`etlplus load` behavior.
250
+ control the emitted format for STDOUT or other non-file outputs, just like `etlplus load`. File
251
+ paths continue to infer formats from their extensions. Use `--source-type` to override the inferred
252
+ source connector type and `--target-type` to override the inferred target connector type, matching
253
+ the `etlplus extract`/`etlplus load` behavior.
232
254
 
233
255
  Transform file inputs while overriding connector types:
234
256
  ```bash
235
- etlplus transform --from file examples/data/sample.json \
257
+ etlplus transform \
236
258
  --operations '{"select": ["name", "email"]}' \
237
- --to file -o temp/selected_output.json
259
+ examples/data/sample.json --source-type file \
260
+ temp/selected_output.json --target-type file
238
261
  ```
239
262
 
240
263
  Filter and select fields:
241
264
  ```bash
242
- etlplus transform '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]' \
243
- --operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}'
265
+ etlplus transform \
266
+ --operations '{"filter": {"field": "age", "op": "gt", "value": 26}, "select": ["name"]}' \
267
+ '[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]'
244
268
  ```
245
269
 
246
270
  Sort data:
247
271
  ```bash
248
- etlplus transform examples/data/sample.json --operations '{"sort": {"field": "age", "reverse": true}}'
272
+ etlplus transform \
273
+ --operations '{"sort": {"field": "age", "reverse": true}}' \
274
+ examples/data/sample.json
249
275
  ```
250
276
 
251
277
  Aggregate data:
252
278
  ```bash
253
- etlplus transform examples/data/sample.json --operations '{"aggregate": {"field": "age", "func": "sum"}}'
279
+ etlplus transform \
280
+ --operations '{"aggregate": {"field": "age", "func": "sum"}}' \
281
+ examples/data/sample.json
254
282
  ```
255
283
 
256
284
  Map/rename fields:
257
285
  ```bash
258
- etlplus transform examples/data/sample.json --operations '{"map": {"name": "new_name"}}'
286
+ etlplus transform \
287
+ --operations '{"map": {"name": "new_name"}}' \
288
+ examples/data/sample.json
259
289
  ```
260
290
 
261
291
  #### Load Data
262
292
 
263
- `etlplus load` consumes JSON from stdin; provide only the target argument plus optional flags.
293
+ `etlplus load` consumes JSON from STDIN; provide only the target argument plus optional flags.
264
294
 
265
295
  Load to JSON file:
266
296
  ```bash
267
297
  etlplus extract file examples/data/sample.json \
268
- | etlplus load --to file temp/sample_output.json
298
+ | etlplus load temp/sample_output.json --target-type file
269
299
  ```
270
300
 
271
301
  Load to CSV file:
272
302
  ```bash
273
303
  etlplus extract file examples/data/sample.csv \
274
- | etlplus load --to file temp/sample_output.csv
304
+ | etlplus load temp/sample_output.csv --target-type file
275
305
  ```
276
306
 
277
307
  Load to REST API:
278
308
  ```bash
279
309
  cat examples/data/sample.json \
280
- | etlplus load --to api https://api.example.com/endpoint
310
+ | etlplus load https://api.example.com/endpoint --target-type api
281
311
  ```
282
312
 
283
313
  ### Python API
@@ -330,20 +360,22 @@ etlplus run --config examples/configs/pipeline.yml --job file_to_file_customers
330
360
 
331
361
  ```bash
332
362
  # 1. Extract from CSV
333
- etlplus extract file examples/data/sample.csv -o temp/sample_extracted.json
363
+ etlplus extract file examples/data/sample.csv > temp/sample_extracted.json
334
364
 
335
365
  # 2. Transform (filter and select fields)
336
- etlplus transform temp/sample_extracted.json \
366
+ etlplus transform \
337
367
  --operations '{"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}' \
338
- -o temp/sample_transformed.json
368
+ temp/sample_extracted.json \
369
+ temp/sample_transformed.json
339
370
 
340
371
  # 3. Validate transformed data
341
- etlplus validate temp/sample_transformed.json \
342
- --rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}'
372
+ etlplus validate \
373
+ --rules '{"name": {"type": "string", "required": true}, "email": {"type": "string", "required": true}}' \
374
+ temo/sample_transformed.json
343
375
 
344
376
  # 4. Load to CSV
345
377
  cat temp/sample_transformed.json \
346
- | etlplus load --to temp/sample_output.csv
378
+ | etlplus load temp/sample_output.csv
347
379
  ```
348
380
 
349
381
  ### Format Overrides
@@ -356,14 +388,14 @@ Examples (zsh):
356
388
 
357
389
  ```zsh
358
390
  # Force CSV parsing for an extension-less file
359
- etlplus extract --from file data.txt --source-format csv
391
+ etlplus extract data.txt --source-type file --source-format csv
360
392
 
361
393
  # Write CSV to a file without the .csv suffix
362
- etlplus load --to file output.bin --target-format csv < data.json
394
+ etlplus load output.bin --target-type file --target-format csv < data.json
363
395
 
364
396
  # Leave the flags off when extensions already match the desired format
365
- etlplus extract --from file data.csv
366
- etlplus load --to file data.json < data.json
397
+ etlplus extract data.csv --source-type file
398
+ etlplus load data.json --target-type file < data.json
367
399
  ```
368
400
 
369
401
  ## Transformation Operations