etlplus 0.13.0__tar.gz → 0.14.0__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 (226) hide show
  1. {etlplus-0.13.0 → etlplus-0.14.0}/CONTRIBUTING.md +1 -1
  2. {etlplus-0.13.0 → etlplus-0.14.0}/DEMO.md +1 -1
  3. {etlplus-0.13.0/etlplus.egg-info → etlplus-0.14.0}/PKG-INFO +4 -4
  4. {etlplus-0.13.0 → etlplus-0.14.0}/README.md +3 -3
  5. {etlplus-0.13.0 → etlplus-0.14.0}/docs/pipeline-guide.md +5 -5
  6. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/README.md +1 -1
  7. etlplus-0.14.0/etlplus/__init__.py +18 -0
  8. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/__init__.py +8 -0
  9. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/endpoint_client.py +3 -3
  10. etlplus-0.13.0/etlplus/run_helpers.py → etlplus-0.14.0/etlplus/api/utils.py +121 -79
  11. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/handlers.py +17 -7
  12. {etlplus-0.13.0/etlplus/validation → etlplus-0.14.0/etlplus/ops}/README.md +2 -2
  13. etlplus-0.14.0/etlplus/ops/__init__.py +57 -0
  14. {etlplus-0.13.0/etlplus → etlplus-0.14.0/etlplus/ops}/extract.py +78 -94
  15. {etlplus-0.13.0/etlplus → etlplus-0.14.0/etlplus/ops}/load.py +73 -93
  16. {etlplus-0.13.0/etlplus → etlplus-0.14.0/etlplus/ops}/run.py +14 -103
  17. {etlplus-0.13.0/etlplus → etlplus-0.14.0/etlplus/ops}/transform.py +75 -68
  18. {etlplus-0.13.0/etlplus/validation → etlplus-0.14.0/etlplus/ops}/utils.py +62 -15
  19. {etlplus-0.13.0/etlplus → etlplus-0.14.0/etlplus/ops}/validate.py +19 -9
  20. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/types.py +2 -2
  21. {etlplus-0.13.0 → etlplus-0.14.0/etlplus.egg-info}/PKG-INFO +4 -4
  22. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus.egg-info/SOURCES.txt +16 -16
  23. {etlplus-0.13.0 → etlplus-0.14.0}/examples/README.md +2 -2
  24. {etlplus-0.13.0 → etlplus-0.14.0}/examples/configs/pipeline.yml +4 -4
  25. {etlplus-0.13.0 → etlplus-0.14.0}/examples/quickstart_python.py +5 -5
  26. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/conftest.py +4 -4
  27. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_pagination_strategy.py +2 -2
  28. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_run.py +1 -1
  29. etlplus-0.13.0/tests/unit/test_u_run_helpers.py → etlplus-0.14.0/tests/unit/api/test_u_api_utils.py +17 -15
  30. etlplus-0.13.0/tests/unit/test_u_extract.py → etlplus-0.14.0/tests/unit/ops/test_u_ops_extract.py +12 -12
  31. etlplus-0.13.0/tests/unit/test_u_load.py → etlplus-0.14.0/tests/unit/ops/test_u_ops_load.py +17 -16
  32. etlplus-0.13.0/tests/unit/test_u_run.py → etlplus-0.14.0/tests/unit/ops/test_u_ops_run.py +4 -4
  33. etlplus-0.13.0/tests/unit/test_u_transform.py → etlplus-0.14.0/tests/unit/ops/test_u_ops_transform.py +65 -64
  34. etlplus-0.13.0/tests/unit/validation/test_u_validation_utils.py → etlplus-0.14.0/tests/unit/ops/test_u_ops_utils.py +4 -4
  35. etlplus-0.13.0/tests/unit/test_u_validate.py → etlplus-0.14.0/tests/unit/ops/test_u_ops_validate.py +9 -9
  36. etlplus-0.13.0/etlplus/__init__.py +0 -43
  37. etlplus-0.13.0/etlplus/validation/__init__.py +0 -44
  38. {etlplus-0.13.0 → etlplus-0.14.0}/.coveragerc +0 -0
  39. {etlplus-0.13.0 → etlplus-0.14.0}/.editorconfig +0 -0
  40. {etlplus-0.13.0 → etlplus-0.14.0}/.gitattributes +0 -0
  41. {etlplus-0.13.0 → etlplus-0.14.0}/.github/actions/python-bootstrap/action.yml +0 -0
  42. {etlplus-0.13.0 → etlplus-0.14.0}/.github/workflows/ci.yml +0 -0
  43. {etlplus-0.13.0 → etlplus-0.14.0}/.gitignore +0 -0
  44. {etlplus-0.13.0 → etlplus-0.14.0}/.pre-commit-config.yaml +0 -0
  45. {etlplus-0.13.0 → etlplus-0.14.0}/.ruff.toml +0 -0
  46. {etlplus-0.13.0 → etlplus-0.14.0}/CODE_OF_CONDUCT.md +0 -0
  47. {etlplus-0.13.0 → etlplus-0.14.0}/LICENSE +0 -0
  48. {etlplus-0.13.0 → etlplus-0.14.0}/MANIFEST.in +0 -0
  49. {etlplus-0.13.0 → etlplus-0.14.0}/Makefile +0 -0
  50. {etlplus-0.13.0 → etlplus-0.14.0}/REFERENCES.md +0 -0
  51. {etlplus-0.13.0 → etlplus-0.14.0}/SECURITY.md +0 -0
  52. {etlplus-0.13.0 → etlplus-0.14.0}/SUPPORT.md +0 -0
  53. {etlplus-0.13.0 → etlplus-0.14.0}/docs/README.md +0 -0
  54. {etlplus-0.13.0 → etlplus-0.14.0}/docs/snippets/installation_version.md +0 -0
  55. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/__main__.py +0 -0
  56. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/__version__.py +0 -0
  57. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/README.md +0 -0
  58. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/auth.py +0 -0
  59. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/config.py +0 -0
  60. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/errors.py +0 -0
  61. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/pagination/__init__.py +0 -0
  62. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/pagination/client.py +0 -0
  63. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/pagination/config.py +0 -0
  64. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/pagination/paginator.py +0 -0
  65. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/rate_limiting/__init__.py +0 -0
  66. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/rate_limiting/config.py +0 -0
  67. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/rate_limiting/rate_limiter.py +0 -0
  68. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/request_manager.py +0 -0
  69. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/retry_manager.py +0 -0
  70. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/transport.py +0 -0
  71. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/api/types.py +0 -0
  72. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/README.md +0 -0
  73. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/__init__.py +0 -0
  74. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/commands.py +0 -0
  75. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/constants.py +0 -0
  76. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/io.py +0 -0
  77. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/main.py +0 -0
  78. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/options.py +0 -0
  79. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/state.py +0 -0
  80. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/cli/types.py +0 -0
  81. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/README.md +0 -0
  82. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/__init__.py +0 -0
  83. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/connector.py +0 -0
  84. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/jobs.py +0 -0
  85. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/pipeline.py +0 -0
  86. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/profile.py +0 -0
  87. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/types.py +0 -0
  88. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/config/utils.py +0 -0
  89. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/dag.py +0 -0
  90. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/database/README.md +0 -0
  91. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/database/__init__.py +0 -0
  92. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/database/ddl.py +0 -0
  93. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/database/engine.py +0 -0
  94. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/database/orm.py +0 -0
  95. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/database/schema.py +0 -0
  96. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/database/types.py +0 -0
  97. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/enums.py +0 -0
  98. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/README.md +0 -0
  99. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/__init__.py +0 -0
  100. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/_imports.py +0 -0
  101. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/_io.py +0 -0
  102. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/accdb.py +0 -0
  103. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/arrow.py +0 -0
  104. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/avro.py +0 -0
  105. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/bson.py +0 -0
  106. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/cbor.py +0 -0
  107. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/cfg.py +0 -0
  108. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/conf.py +0 -0
  109. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/core.py +0 -0
  110. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/csv.py +0 -0
  111. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/dat.py +0 -0
  112. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/dta.py +0 -0
  113. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/duckdb.py +0 -0
  114. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/enums.py +0 -0
  115. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/feather.py +0 -0
  116. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/fwf.py +0 -0
  117. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/gz.py +0 -0
  118. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/hbs.py +0 -0
  119. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/hdf5.py +0 -0
  120. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/ini.py +0 -0
  121. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/ion.py +0 -0
  122. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/jinja2.py +0 -0
  123. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/json.py +0 -0
  124. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/log.py +0 -0
  125. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/mat.py +0 -0
  126. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/mdb.py +0 -0
  127. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/msgpack.py +0 -0
  128. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/mustache.py +0 -0
  129. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/nc.py +0 -0
  130. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/ndjson.py +0 -0
  131. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/numbers.py +0 -0
  132. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/ods.py +0 -0
  133. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/orc.py +0 -0
  134. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/parquet.py +0 -0
  135. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/pb.py +0 -0
  136. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/pbf.py +0 -0
  137. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/properties.py +0 -0
  138. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/proto.py +0 -0
  139. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/psv.py +0 -0
  140. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/rda.py +0 -0
  141. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/rds.py +0 -0
  142. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/sas7bdat.py +0 -0
  143. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/sav.py +0 -0
  144. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/sqlite.py +0 -0
  145. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/stub.py +0 -0
  146. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/sylk.py +0 -0
  147. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/tab.py +0 -0
  148. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/toml.py +0 -0
  149. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/tsv.py +0 -0
  150. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/txt.py +0 -0
  151. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/vm.py +0 -0
  152. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/wks.py +0 -0
  153. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/xls.py +0 -0
  154. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/xlsm.py +0 -0
  155. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/xlsx.py +0 -0
  156. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/xml.py +0 -0
  157. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/xpt.py +0 -0
  158. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/yaml.py +0 -0
  159. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/zip.py +0 -0
  160. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/file/zsav.py +0 -0
  161. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/mixins.py +0 -0
  162. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/py.typed +0 -0
  163. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/templates/README.md +0 -0
  164. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/templates/__init__.py +0 -0
  165. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/templates/ddl.sql.j2 +0 -0
  166. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/templates/view.sql.j2 +0 -0
  167. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus/utils.py +0 -0
  168. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus.egg-info/dependency_links.txt +0 -0
  169. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus.egg-info/entry_points.txt +0 -0
  170. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus.egg-info/requires.txt +0 -0
  171. {etlplus-0.13.0 → etlplus-0.14.0}/etlplus.egg-info/top_level.txt +0 -0
  172. {etlplus-0.13.0 → etlplus-0.14.0}/examples/configs/ddl_spec.yml +0 -0
  173. {etlplus-0.13.0 → etlplus-0.14.0}/examples/data/sample.csv +0 -0
  174. {etlplus-0.13.0 → etlplus-0.14.0}/examples/data/sample.json +0 -0
  175. {etlplus-0.13.0 → etlplus-0.14.0}/examples/data/sample.xml +0 -0
  176. {etlplus-0.13.0 → etlplus-0.14.0}/examples/data/sample.xsd +0 -0
  177. {etlplus-0.13.0 → etlplus-0.14.0}/examples/data/sample.yaml +0 -0
  178. {etlplus-0.13.0 → etlplus-0.14.0}/pyproject.toml +0 -0
  179. {etlplus-0.13.0 → etlplus-0.14.0}/pytest.ini +0 -0
  180. {etlplus-0.13.0 → etlplus-0.14.0}/setup.cfg +0 -0
  181. {etlplus-0.13.0 → etlplus-0.14.0}/setup.py +0 -0
  182. {etlplus-0.13.0 → etlplus-0.14.0}/tests/__init__.py +0 -0
  183. {etlplus-0.13.0 → etlplus-0.14.0}/tests/conftest.py +0 -0
  184. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_cli.py +0 -0
  185. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_examples_data_parity.py +0 -0
  186. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_pipeline_smoke.py +0 -0
  187. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_pipeline_yaml_load.py +0 -0
  188. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_run_profile_pagination_defaults.py +0 -0
  189. {etlplus-0.13.0 → etlplus-0.14.0}/tests/integration/test_i_run_profile_rate_limit_defaults.py +0 -0
  190. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/conftest.py +0 -0
  191. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_auth.py +0 -0
  192. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_config.py +0 -0
  193. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_endpoint_client.py +0 -0
  194. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_mocks.py +0 -0
  195. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_pagination_client.py +0 -0
  196. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_pagination_config.py +0 -0
  197. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_paginator.py +0 -0
  198. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_rate_limit_config.py +0 -0
  199. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_rate_limiter.py +0 -0
  200. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_request_manager.py +0 -0
  201. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_retry_manager.py +0 -0
  202. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_transport.py +0 -0
  203. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/api/test_u_types.py +0 -0
  204. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/cli/conftest.py +0 -0
  205. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/cli/test_u_cli_handlers.py +0 -0
  206. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/cli/test_u_cli_io.py +0 -0
  207. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/cli/test_u_cli_main.py +0 -0
  208. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/cli/test_u_cli_state.py +0 -0
  209. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/config/test_u_config_utils.py +0 -0
  210. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/config/test_u_connector.py +0 -0
  211. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/config/test_u_jobs.py +0 -0
  212. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/config/test_u_pipeline.py +0 -0
  213. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/conftest.py +0 -0
  214. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/database/test_u_database_ddl.py +0 -0
  215. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/database/test_u_database_engine.py +0 -0
  216. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/database/test_u_database_orm.py +0 -0
  217. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/database/test_u_database_schema.py +0 -0
  218. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/file/test_u_file_core.py +0 -0
  219. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/file/test_u_file_enums.py +0 -0
  220. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/file/test_u_file_yaml.py +0 -0
  221. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/test_u_enums.py +0 -0
  222. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/test_u_main.py +0 -0
  223. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/test_u_mixins.py +0 -0
  224. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/test_u_utils.py +0 -0
  225. {etlplus-0.13.0 → etlplus-0.14.0}/tests/unit/test_u_version.py +0 -0
  226. {etlplus-0.13.0 → etlplus-0.14.0}/tools/update_demo_snippets.py +0 -0
@@ -119,7 +119,7 @@ Use these guidelines to decide whether a test belongs in the unit or integration
119
119
  - Can use temporary files/directories, and stub network with fakes/mocks.
120
120
  - Examples in this repo: CLI end-to-end, pipeline smoke tests, pagination strategy, runner defaults for pagination/rate limits, target URL composition.
121
121
 
122
- If a test calls `etlplus.cli.main()` or `etlplus.run.run()`, it is integration by default.
122
+ If a test calls `etlplus.cli.main()` or `etlplus.ops.run.run()`, it is integration by default.
123
123
 
124
124
  ### Where to put tests
125
125
 
@@ -196,7 +196,7 @@ $ etlplus load transformed.json file final_output.csv
196
196
  ## Demo 6: Using Python API
197
197
 
198
198
  ```python
199
- from etlplus import extract, validate, transform, load
199
+ from etlplus.ops import extract, validate, transform, load
200
200
 
201
201
  # Extract
202
202
  data = extract("file", "data.csv", format="csv")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.13.0
3
+ Version: 0.14.0
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
@@ -196,7 +196,7 @@ etlplus extract file examples/data/sample.csv \
196
196
  [Python API](#python-api):
197
197
 
198
198
  ```python
199
- from etlplus import extract, transform, validate, load
199
+ from etlplus.ops import extract, transform, validate, load
200
200
 
201
201
  data = extract("file", "input.csv")
202
202
  ops = {"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}
@@ -531,7 +531,7 @@ cat examples/data/sample.json \
531
531
  Use ETLPlus as a Python library:
532
532
 
533
533
  ```python
534
- from etlplus import extract, validate, transform, load
534
+ from etlplus.ops import extract, validate, transform, load
535
535
 
536
536
  # Extract data
537
537
  data = extract("file", "data.json")
@@ -726,7 +726,7 @@ We split tests into two layers:
726
726
  pagination + rate limit defaults, file/API connector interactions) may touch temp files and use
727
727
  fake clients.
728
728
 
729
- If a test calls `etlplus.cli.main()` or `etlplus.run.run()` it’s integration by default. Full
729
+ If a test calls `etlplus.cli.main()` or `etlplus.ops.run.run()` it’s integration by default. Full
730
730
  criteria: [`CONTRIBUTING.md#testing`](CONTRIBUTING.md#testing).
731
731
 
732
732
  ### Code Coverage
@@ -146,7 +146,7 @@ etlplus extract file examples/data/sample.csv \
146
146
  [Python API](#python-api):
147
147
 
148
148
  ```python
149
- from etlplus import extract, transform, validate, load
149
+ from etlplus.ops import extract, transform, validate, load
150
150
 
151
151
  data = extract("file", "input.csv")
152
152
  ops = {"filter": {"field": "age", "op": "gt", "value": 25}, "select": ["name", "email"]}
@@ -481,7 +481,7 @@ cat examples/data/sample.json \
481
481
  Use ETLPlus as a Python library:
482
482
 
483
483
  ```python
484
- from etlplus import extract, validate, transform, load
484
+ from etlplus.ops import extract, validate, transform, load
485
485
 
486
486
  # Extract data
487
487
  data = extract("file", "data.json")
@@ -676,7 +676,7 @@ We split tests into two layers:
676
676
  pagination + rate limit defaults, file/API connector interactions) may touch temp files and use
677
677
  fake clients.
678
678
 
679
- If a test calls `etlplus.cli.main()` or `etlplus.run.run()` it’s integration by default. Full
679
+ If a test calls `etlplus.cli.main()` or `etlplus.ops.run.run()` it’s integration by default. Full
680
680
  criteria: [`CONTRIBUTING.md#testing`](CONTRIBUTING.md#testing).
681
681
 
682
682
  ### Code Coverage
@@ -281,7 +281,7 @@ section.
281
281
 
282
282
  ## Validations
283
283
 
284
- Validation rule sets map field names to rules, mirroring `etlplus.validate.FieldRules`:
284
+ Validation rule sets map field names to rules, mirroring `etlplus.ops.validate.FieldRules`:
285
285
 
286
286
  ```yaml
287
287
  validations:
@@ -297,7 +297,7 @@ validations:
297
297
 
298
298
  ## Transforms
299
299
 
300
- Transformation pipelines follow `etlplus.transform` shapes exactly:
300
+ Transformation pipelines follow `etlplus.ops.transform` shapes exactly:
301
301
 
302
302
  ```yaml
303
303
  transforms:
@@ -431,14 +431,14 @@ Notes:
431
431
  - Environment-variable substitution (e.g. `${GITHUB_TOKEN}`) is applied the same way as when loading
432
432
  configs via the Python API.
433
433
  - For more details on the orchestration implementation, see
434
- [Runner internals: etlplus.run](run-module.md).
434
+ [Runner internals: etlplus.ops.run](run-module.md).
435
435
 
436
- ### Python: `etlplus.run.run`
436
+ ### Python: `etlplus.ops.run.run`
437
437
 
438
438
  To trigger a job programmatically, use the high-level runner function exposed by the package:
439
439
 
440
440
  ```python
441
- from etlplus.run import run as run_job
441
+ from etlplus.ops.run import run as run_job
442
442
 
443
443
  result = run_job(
444
444
  job="file_to_file_customers",
@@ -23,7 +23,7 @@ Back to project overview: see the top-level [README](../README.md).
23
23
  ## Quickstart
24
24
 
25
25
  ```python
26
- from etlplus import extract, validate, transform, load
26
+ from etlplus.ops import extract, validate, transform, load
27
27
 
28
28
  data = extract("file", "input.csv")
29
29
  filtered = transform(data, {"filter": {"field": "age", "op": "gt", "value": 25}})
@@ -0,0 +1,18 @@
1
+ """
2
+ :mod:`etlplus` package.
3
+
4
+ Top-level facade for the ETLPlus toolkit.
5
+ """
6
+
7
+ from .__version__ import __version__
8
+
9
+ __author__ = 'ETLPlus Team'
10
+
11
+
12
+ # SECTION: EXPORTS ========================================================== #
13
+
14
+
15
+ __all__ = [
16
+ '__author__',
17
+ '__version__',
18
+ ]
@@ -98,6 +98,10 @@ from .types import Headers
98
98
  from .types import Params
99
99
  from .types import RequestOptions
100
100
  from .types import Url
101
+ from .utils import compose_api_request_env
102
+ from .utils import compose_api_target_env
103
+ from .utils import paginate_with_client
104
+ from .utils import resolve_request
101
105
 
102
106
  # SECTION: EXPORTS ========================================================== #
103
107
 
@@ -122,6 +126,10 @@ __all__ = [
122
126
  'PaginationType',
123
127
  # Functions
124
128
  'build_http_adapter',
129
+ 'compose_api_request_env',
130
+ 'compose_api_target_env',
131
+ 'paginate_with_client',
132
+ 'resolve_request',
125
133
  # Type Aliases
126
134
  'CursorPaginationConfigMap',
127
135
  'Headers',
@@ -455,7 +455,7 @@ class EndpointClient:
455
455
  -------
456
456
  JSONData
457
457
  Parsed JSON payload or fallback structure matching
458
- :func:`etlplus.extract.extract_from_api` semantics.
458
+ :func:`etlplus.ops.extract.extract_from_api` semantics.
459
459
  """
460
460
  return self._request_manager.get(url, **kwargs)
461
461
 
@@ -479,7 +479,7 @@ class EndpointClient:
479
479
  -------
480
480
  JSONData
481
481
  Parsed JSON payload or fallback structure matching
482
- :func:`etlplus.extract.extract_from_api` semantics.
482
+ :func:`etlplus.ops.extract.extract_from_api` semantics.
483
483
  """
484
484
  return self._request_manager.post(url, **kwargs)
485
485
 
@@ -506,7 +506,7 @@ class EndpointClient:
506
506
  -------
507
507
  JSONData
508
508
  Parsed JSON payload or fallback structure matching
509
- :func:`etlplus.extract.extract_from_api` semantics.
509
+ :func:`etlplus.ops.extract.extract_from_api` semantics.
510
510
  """
511
511
  return self._request_manager.request(method, url, **kwargs)
512
512
 
@@ -1,30 +1,13 @@
1
1
  """
2
- :mod:`etlplus.run_helpers` module.
3
-
4
- Helper functions and small utilities used by ``etlplus.run`` to compose API
5
- request/load environments, pagination configs, session objects, and endpoint
6
- clients. Extracted to keep ``run.py`` focused on orchestration while enabling
7
- reuse and testability.
8
-
9
- Public (re-export safe) helpers:
10
- - build_pagination_cfg(pagination, overrides)
11
- - build_session(cfg)
12
- - compose_api_request_env(cfg, source_obj, extract_opts)
13
- - compose_api_target_env(cfg, target_obj, overrides)
14
- - build_endpoint_client(base_url, base_path, endpoints, env)
15
- - compute_rl_sleep_seconds(rate_limit, overrides)
16
- - paginate_with_client(client, endpoint_key, params, headers,
17
- timeout, pagination, sleep_seconds)
18
-
19
- Notes
20
- -----
21
- These helpers intentionally accept permissive ``Any``/``Mapping`` inputs to
22
- avoid tight coupling with config dataclasses while keeping runtime flexible.
2
+ :mod:`etlplus.api.utils` module.
3
+
4
+ Shared HTTP helpers for API clients that communicate with REST endpoints.
23
5
  """
24
6
 
25
7
  from __future__ import annotations
26
8
 
27
9
  import inspect
10
+ from collections.abc import Callable
28
11
  from collections.abc import Mapping
29
12
  from typing import Any
30
13
  from typing import TypedDict
@@ -32,24 +15,33 @@ from typing import cast
32
15
 
33
16
  import requests # type: ignore[import]
34
17
 
35
- from .api import ApiConfig
36
- from .api import EndpointClient
37
- from .api import EndpointConfig
38
- from .api import Headers
39
- from .api import PaginationConfig
40
- from .api import PaginationConfigMap
41
- from .api import Params
42
- from .api import RateLimitConfig
43
- from .api import RateLimitConfigMap
44
- from .api import RateLimiter
45
- from .api import RetryPolicy
46
- from .api import Url
47
- from .types import Timeout
18
+ from ..enums import HttpMethod
19
+ from ..types import Timeout
20
+ from .config import ApiConfig
21
+ from .config import EndpointConfig
22
+ from .endpoint_client import EndpointClient
23
+ from .pagination import PaginationConfig
24
+ from .pagination import PaginationConfigMap
25
+ from .rate_limiting import RateLimitConfig
26
+ from .rate_limiting import RateLimitConfigMap
27
+ from .rate_limiting import RateLimiter
28
+ from .retry_manager import RetryPolicy
29
+ from .types import Headers
30
+ from .types import Params
31
+ from .types import Url
32
+
33
+ # SECTION: CONSTANTS ======================================================== #
34
+
35
+
36
+ DEFAULT_TIMEOUT: float = 10.0
37
+
48
38
 
49
39
  # SECTION: EXPORTS ========================================================== #
50
40
 
51
41
 
52
42
  __all__ = [
43
+ # Constants
44
+ 'DEFAULT_TIMEOUT',
53
45
  # Functions
54
46
  'build_endpoint_client',
55
47
  'build_pagination_cfg',
@@ -58,6 +50,7 @@ __all__ = [
58
50
  'compose_api_target_env',
59
51
  'compute_rl_sleep_seconds',
60
52
  'paginate_with_client',
53
+ 'resolve_request',
61
54
  # Typed Dicts
62
55
  'ApiRequestEnv',
63
56
  'ApiTargetEnv',
@@ -68,43 +61,83 @@ __all__ = [
68
61
  # SECTION: TYPED DICTS ====================================================== #
69
62
 
70
63
 
71
- class ApiRequestEnv(TypedDict, total=False):
72
- """API request environment configuration."""
64
+ class BaseApiHttpEnv(TypedDict, total=False):
65
+ """
66
+ Common HTTP request environment for API interactions.
67
+
68
+ Fields shared by both source-side and target-side API operations.
69
+ """
73
70
 
71
+ # Request details
74
72
  url: Url | None
75
73
  headers: dict[str, str]
76
74
  timeout: Timeout
75
+
76
+ # Session
77
77
  session: requests.Session | None
78
+
79
+
80
+ class ApiRequestEnv(BaseApiHttpEnv, total=False):
81
+ """
82
+ Composed HTTP request environment configuration for REST API sources.
83
+
84
+ Returned by :func:`compose_api_request_env` and consumed by the API extract
85
+ branch. Values are fully merged with endpoint/API defaults and job-level
86
+ overrides, preserving the original precedence and behavior.
87
+ """
88
+
89
+ # Client
78
90
  use_endpoints: bool
79
91
  base_url: str | None
80
92
  base_path: str | None
81
93
  endpoints_map: dict[str, str] | None
82
94
  endpoint_key: str | None
95
+
96
+ # Request
83
97
  params: dict[str, Any]
84
98
  pagination: PaginationConfigMap | None
85
99
  sleep_seconds: float
100
+
101
+ # Reliability
86
102
  retry: RetryPolicy | None
87
103
  retry_network_errors: bool
88
104
 
89
105
 
90
- class ApiTargetEnv(TypedDict, total=False):
91
- """API target environment configuration."""
106
+ class ApiTargetEnv(BaseApiHttpEnv, total=False):
107
+ """
108
+ Composed HTTP request environment configuration for REST API targets.
109
+
110
+ Returned by :func:`compose_api_target_env` and consumed by the API load
111
+ branch. Values are merged from the target object, optional API/endpoint
112
+ reference, and job-level overrides, preserving original precedence and
113
+ behavior.
114
+
115
+ Notes
116
+ -----
117
+ - Precedence for inherited values matches original logic:
118
+ overrides -> target -> API profile defaults.
119
+ - Target composition does not include pagination/rate-limit/retry since
120
+ loads are single-request operations; only headers/timeout/session
121
+ apply.
122
+ """
92
123
 
93
- url: Url | None
94
- headers: dict[str, str]
95
- timeout: Timeout
96
- session: requests.Session | None
124
+ # Request
97
125
  method: str | None
98
126
 
99
127
 
100
128
  class SessionConfig(TypedDict, total=False):
101
- """Configuration for requests.Session."""
129
+ """
130
+ Minimal session configuration schema accepted by the
131
+ :class:`requests.Session` runner.
132
+
133
+ Keys mirror common :class:`requests.Session` options; all are optional.
134
+ """
102
135
 
103
136
  headers: Mapping[str, Any]
104
137
  params: Mapping[str, Any]
105
- auth: Any
138
+ auth: Any # (user, pass) tuple or requests-compatible auth object
106
139
  verify: bool | str
107
- cert: Any
140
+ cert: Any # str or (cert, key)
108
141
  proxies: Mapping[str, Any]
109
142
  cookies: Mapping[str, Any]
110
143
  trust_env: bool
@@ -113,9 +146,6 @@ class SessionConfig(TypedDict, total=False):
113
146
  # SECTION: INTERNAL FUNCTIONS ============================================== #
114
147
 
115
148
 
116
- # -- API Environment Composition -- #
117
-
118
-
119
149
  def _get_api_cfg_and_endpoint(
120
150
  cfg: Any,
121
151
  api_name: str,
@@ -226,9 +256,6 @@ def _merge_session_cfg_three(
226
256
  return cast(SessionConfig | None, (merged or None))
227
257
 
228
258
 
229
- # -- Mapping Helpers -- #
230
-
231
-
232
259
  def _copy_mapping(
233
260
  mapping: Mapping[str, Any] | None,
234
261
  ) -> dict[str, Any]:
@@ -266,9 +293,6 @@ def _update_mapping(
266
293
  target.update(extra)
267
294
 
268
295
 
269
- # -- Session -- #
270
-
271
-
272
296
  def _build_session_optional(
273
297
  cfg: SessionConfig | None,
274
298
  ) -> requests.Session | None:
@@ -285,7 +309,6 @@ def _build_session_optional(
285
309
  requests.Session | None
286
310
  Configured session or ``None``.
287
311
  """
288
-
289
312
  if isinstance(cfg, dict):
290
313
  return build_session(cfg)
291
314
  return None
@@ -294,9 +317,6 @@ def _build_session_optional(
294
317
  # SECTION: FUNCTIONS ======================================================== #
295
318
 
296
319
 
297
- # -- API Environment Composition -- #
298
-
299
-
300
320
  def build_endpoint_client(
301
321
  *,
302
322
  base_url: str,
@@ -323,15 +343,7 @@ def build_endpoint_client(
323
343
  EndpointClient
324
344
  The constructed endpoint client.
325
345
  """
326
- # Allow tests to monkeypatch etlplus.run.EndpointClient and have it
327
- # propagate here by preferring the class on the run module if present.
328
- try:
329
- from . import run as run_mod # local import to avoid cycles
330
-
331
- ClientClass = getattr(run_mod, 'EndpointClient', EndpointClient)
332
- except (ImportError, AttributeError): # pragma: no cover - fallback path
333
- ClientClass = EndpointClient
334
- return ClientClass(
346
+ return EndpointClient(
335
347
  base_url=base_url,
336
348
  base_path=base_path,
337
349
  endpoints=endpoints,
@@ -558,9 +570,6 @@ def compose_api_target_env(
558
570
  }
559
571
 
560
572
 
561
- # -- Pagination -- #
562
-
563
-
564
573
  def build_pagination_cfg(
565
574
  pagination: PaginationConfig | None,
566
575
  overrides: Mapping[str, Any] | None,
@@ -667,9 +676,6 @@ def build_pagination_cfg(
667
676
  return cast(PaginationConfigMap, cfg)
668
677
 
669
678
 
670
- # -- Pagination Invocation -- #
671
-
672
-
673
679
  def paginate_with_client(
674
680
  client: Any,
675
681
  endpoint_key: str,
@@ -727,9 +733,6 @@ def paginate_with_client(
727
733
  return client.paginate(endpoint_key, **kw_pag)
728
734
 
729
735
 
730
- # -- Rate Limit -- #
731
-
732
-
733
736
  def compute_rl_sleep_seconds(
734
737
  rate_limit: RateLimitConfig | Mapping[str, Any] | None,
735
738
  overrides: Mapping[str, Any] | None,
@@ -782,9 +785,6 @@ def compute_rl_sleep_seconds(
782
785
  )
783
786
 
784
787
 
785
- # -- Session -- #
786
-
787
-
788
788
  def build_session(
789
789
  cfg: SessionConfig | None,
790
790
  ) -> requests.Session:
@@ -841,3 +841,45 @@ def build_session(
841
841
  pass
842
842
 
843
843
  return s
844
+
845
+
846
+ def resolve_request(
847
+ method: HttpMethod | str,
848
+ *,
849
+ session: Any | None = None,
850
+ timeout: Timeout = None,
851
+ ) -> tuple[Callable[..., requests.Response], float, HttpMethod]:
852
+ """
853
+ Resolve a request callable and effective timeout for an HTTP method.
854
+
855
+ Parameters
856
+ ----------
857
+ method : HttpMethod | str
858
+ HTTP method to execute.
859
+ session : Any | None, optional
860
+ Requests-compatible session object. Defaults to module-level
861
+ ``requests``.
862
+ timeout : Timeout, optional
863
+ Timeout in seconds for the request. Uses ``DEFAULT_TIMEOUT`` when
864
+ omitted.
865
+
866
+ Returns
867
+ -------
868
+ tuple[Callable[..., requests.Response], float, HttpMethod]
869
+ Tuple of (callable, timeout_seconds, resolved_method).
870
+
871
+ Raises
872
+ ------
873
+ TypeError
874
+ If the session object does not expose the requested HTTP method.
875
+ """
876
+ http_method = HttpMethod.coerce(method)
877
+ request_timeout = DEFAULT_TIMEOUT if timeout is None else timeout
878
+ requester = session or requests
879
+ request_callable = getattr(requester, http_method.value, None)
880
+ if not callable(request_callable):
881
+ raise TypeError(
882
+ 'Session object must supply a callable '
883
+ f'"{http_method.value}" method',
884
+ )
885
+ return request_callable, request_timeout, http_method
@@ -18,15 +18,16 @@ from ..config import PipelineConfig
18
18
  from ..config import load_pipeline_config
19
19
  from ..database import load_table_spec
20
20
  from ..database import render_tables
21
- from ..extract import extract
22
21
  from ..file import File
23
- from ..load import load
24
- from ..run import run
25
- from ..transform import transform
22
+ from ..file import FileFormat
23
+ from ..ops import extract
24
+ from ..ops import load
25
+ from ..ops import run
26
+ from ..ops import transform
27
+ from ..ops import validate
28
+ from ..ops.validate import FieldRules
26
29
  from ..types import JSONData
27
30
  from ..types import TemplateKey
28
- from ..validate import FieldRules
29
- from ..validate import validate
30
31
  from . import io as cli_io
31
32
 
32
33
  # SECTION: EXPORTS ========================================================== #
@@ -569,8 +570,17 @@ def transform_handler(
569
570
 
570
571
  data = transform(payload, cast(TransformOperations, operations_payload))
571
572
 
573
+ # TODO: Generalize to handle non-file targets.
572
574
  if target and target != '-':
573
- File(target, file_format=target_format).write(data)
575
+ # Convert target to Path and target_format to FileFormat if needed
576
+ file_path = Path(target)
577
+ file_format = None
578
+ if target_format is not None:
579
+ try:
580
+ file_format = FileFormat(target_format)
581
+ except ValueError:
582
+ file_format = None # or handle error as appropriate
583
+ File(file_path, file_format=file_format).write(data)
574
584
  print(f'Data transformed and saved to {target}')
575
585
  return 0
576
586
 
@@ -1,4 +1,4 @@
1
- # etlplus.validation subpackage
1
+ # etlplus.ops subpackage
2
2
 
3
3
  Documentation for the `etlplus.validation` subpackage: data validation utilities and helpers.
4
4
 
@@ -8,7 +8,7 @@ Documentation for the `etlplus.validation` subpackage: data validation utilities
8
8
 
9
9
  Back to project overview: see the top-level [README](../../README.md).
10
10
 
11
- - [etlplus.validation subpackage](#etlplusvalidation-subpackage)
11
+ - [etlplus.ops subpackage](#etlplusops-subpackage)
12
12
  - [Validation Features](#validation-features)
13
13
  - [Defining Validation Rules](#defining-validation-rules)
14
14
  - [Example: Validating Data](#example-validating-data)
@@ -0,0 +1,57 @@
1
+ """
2
+ :mod:`etlplus.ops` package.
3
+
4
+ Data operations helpers.
5
+
6
+ Importing :mod:`etlplus.ops` exposes the coarse-grained helpers most users care
7
+ about: ``extract``, ``transform``, ``load``, ``validate``, and ``run``. Each
8
+ helper delegates to the richer modules under ``etlplus.ops.*`` while
9
+ presenting a compact public API surface. Conditional validation orchestration
10
+ is available via :func:`etlplus.ops.utils.maybe_validate`.
11
+
12
+ Examples
13
+ --------
14
+ >>> from etlplus.ops import extract, transform
15
+ >>> raw = extract('file', 'input.json')
16
+ >>> curated = transform(raw, {'select': ['id', 'name']})
17
+
18
+ >>> from etlplus.ops.utils import maybe_validate
19
+ >>> payload = {'name': 'Alice'}
20
+ >>> rules = {'required': ['name']}
21
+ >>> def validator(data, config):
22
+ ... missing = [field for field in config['required'] if field not in data]
23
+ ... return {'valid': not missing, 'errors': missing, 'data': data}
24
+ >>> maybe_validate(
25
+ ... payload,
26
+ ... when='both',
27
+ ... enabled=True,
28
+ ... rules=rules,
29
+ ... phase='before_transform',
30
+ ... severity='warn',
31
+ ... validate_fn=validator,
32
+ ... print_json_fn=lambda message: message,
33
+ ... )
34
+ {'name': 'Alice'}
35
+
36
+ See Also
37
+ --------
38
+ :mod:`etlplus.ops.run`
39
+ :mod:`etlplus.ops.utils`
40
+ """
41
+
42
+ from .extract import extract
43
+ from .load import load
44
+ from .run import run
45
+ from .transform import transform
46
+ from .validate import validate
47
+
48
+ # SECTION: EXPORTS ========================================================== #
49
+
50
+
51
+ __all__ = [
52
+ 'extract',
53
+ 'load',
54
+ 'run',
55
+ 'transform',
56
+ 'validate',
57
+ ]