etlplus 0.14.1__tar.gz → 0.15.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 (230) hide show
  1. {etlplus-0.14.1/etlplus.egg-info → etlplus-0.15.0}/PKG-INFO +1 -1
  2. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/README.md +1 -1
  3. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/README.md +2 -2
  4. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/__init__.py +2 -0
  5. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/config.py +36 -20
  6. etlplus-0.15.0/etlplus/api/enums.py +51 -0
  7. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/client.py +1 -1
  8. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/rate_limiting/config.py +13 -1
  9. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/rate_limiting/rate_limiter.py +8 -11
  10. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/request_manager.py +11 -6
  11. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/transport.py +14 -2
  12. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/types.py +7 -6
  13. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/utils.py +92 -78
  14. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/README.md +2 -2
  15. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/handlers.py +2 -2
  16. etlplus-0.15.0/etlplus/config/README.md +50 -0
  17. etlplus-0.15.0/etlplus/config/__init__.py +33 -0
  18. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/config/types.py +0 -64
  19. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/README.md +2 -2
  20. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/enums.py +0 -32
  21. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/README.md +2 -2
  22. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/extract.py +1 -1
  23. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/load.py +1 -1
  24. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/run.py +15 -10
  25. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/utils.py +28 -10
  26. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/README.md +2 -2
  27. {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/README.md +6 -6
  28. {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/__init__.py +10 -23
  29. {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/connector.py +17 -16
  30. etlplus-0.15.0/etlplus/workflow/dag.py +105 -0
  31. {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/jobs.py +17 -11
  32. {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/pipeline.py +11 -3
  33. {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/profile.py +8 -5
  34. etlplus-0.15.0/etlplus/workflow/types.py +115 -0
  35. {etlplus-0.14.1 → etlplus-0.15.0/etlplus.egg-info}/PKG-INFO +1 -1
  36. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/SOURCES.txt +15 -9
  37. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/conftest.py +6 -6
  38. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_pagination_strategy.py +11 -11
  39. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_pipeline_yaml_load.py +1 -1
  40. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_run.py +1 -1
  41. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_run_profile_pagination_defaults.py +1 -1
  42. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_run_profile_rate_limit_defaults.py +1 -1
  43. etlplus-0.15.0/tests/unit/api/test_u_api_enums.py +34 -0
  44. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/conftest.py +2 -2
  45. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_handlers.py +3 -1
  46. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/conftest.py +1 -1
  47. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_load.py +1 -1
  48. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_enums.py +0 -16
  49. etlplus-0.14.1/tests/unit/config/test_u_connector.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_connector.py +6 -6
  50. etlplus-0.14.1/tests/unit/config/test_u_jobs.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_jobs.py +3 -3
  51. etlplus-0.14.1/tests/unit/config/test_u_pipeline.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_pipeline.py +7 -7
  52. etlplus-0.14.1/tests/unit/config/test_u_config_utils.py → etlplus-0.15.0/tests/unit/workflow/test_u_workflow_utils.py +3 -3
  53. {etlplus-0.14.1 → etlplus-0.15.0}/.coveragerc +0 -0
  54. {etlplus-0.14.1 → etlplus-0.15.0}/.editorconfig +0 -0
  55. {etlplus-0.14.1 → etlplus-0.15.0}/.gitattributes +0 -0
  56. {etlplus-0.14.1 → etlplus-0.15.0}/.github/actions/python-bootstrap/action.yml +0 -0
  57. {etlplus-0.14.1 → etlplus-0.15.0}/.github/workflows/ci.yml +0 -0
  58. {etlplus-0.14.1 → etlplus-0.15.0}/.gitignore +0 -0
  59. {etlplus-0.14.1 → etlplus-0.15.0}/.pre-commit-config.yaml +0 -0
  60. {etlplus-0.14.1 → etlplus-0.15.0}/.ruff.toml +0 -0
  61. {etlplus-0.14.1 → etlplus-0.15.0}/CODE_OF_CONDUCT.md +0 -0
  62. {etlplus-0.14.1 → etlplus-0.15.0}/CONTRIBUTING.md +0 -0
  63. {etlplus-0.14.1 → etlplus-0.15.0}/DEMO.md +0 -0
  64. {etlplus-0.14.1 → etlplus-0.15.0}/LICENSE +0 -0
  65. {etlplus-0.14.1 → etlplus-0.15.0}/MANIFEST.in +0 -0
  66. {etlplus-0.14.1 → etlplus-0.15.0}/Makefile +0 -0
  67. {etlplus-0.14.1 → etlplus-0.15.0}/README.md +0 -0
  68. {etlplus-0.14.1 → etlplus-0.15.0}/REFERENCES.md +0 -0
  69. {etlplus-0.14.1 → etlplus-0.15.0}/SECURITY.md +0 -0
  70. {etlplus-0.14.1 → etlplus-0.15.0}/SUPPORT.md +0 -0
  71. {etlplus-0.14.1 → etlplus-0.15.0}/docs/README.md +0 -0
  72. {etlplus-0.14.1 → etlplus-0.15.0}/docs/pipeline-guide.md +0 -0
  73. {etlplus-0.14.1 → etlplus-0.15.0}/docs/snippets/installation_version.md +0 -0
  74. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/__init__.py +0 -0
  75. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/__main__.py +0 -0
  76. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/__version__.py +0 -0
  77. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/auth.py +0 -0
  78. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/endpoint_client.py +0 -0
  79. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/errors.py +0 -0
  80. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/__init__.py +0 -0
  81. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/config.py +0 -0
  82. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/pagination/paginator.py +0 -0
  83. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/rate_limiting/__init__.py +0 -0
  84. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/api/retry_manager.py +0 -0
  85. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/__init__.py +0 -0
  86. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/commands.py +0 -0
  87. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/constants.py +0 -0
  88. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/io.py +0 -0
  89. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/main.py +0 -0
  90. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/options.py +0 -0
  91. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/state.py +0 -0
  92. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/cli/types.py +0 -0
  93. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/dag.py +0 -0
  94. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/__init__.py +0 -0
  95. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/ddl.py +0 -0
  96. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/engine.py +0 -0
  97. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/orm.py +0 -0
  98. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/schema.py +0 -0
  99. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/database/types.py +0 -0
  100. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/__init__.py +0 -0
  101. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/_imports.py +0 -0
  102. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/_io.py +0 -0
  103. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/accdb.py +0 -0
  104. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/arrow.py +0 -0
  105. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/avro.py +0 -0
  106. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/bson.py +0 -0
  107. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/cbor.py +0 -0
  108. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/cfg.py +0 -0
  109. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/conf.py +0 -0
  110. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/core.py +0 -0
  111. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/csv.py +0 -0
  112. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/dat.py +0 -0
  113. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/dta.py +0 -0
  114. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/duckdb.py +0 -0
  115. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/enums.py +0 -0
  116. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/feather.py +0 -0
  117. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/fwf.py +0 -0
  118. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/gz.py +0 -0
  119. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/hbs.py +0 -0
  120. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/hdf5.py +0 -0
  121. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ini.py +0 -0
  122. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ion.py +0 -0
  123. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/jinja2.py +0 -0
  124. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/json.py +0 -0
  125. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/log.py +0 -0
  126. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/mat.py +0 -0
  127. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/mdb.py +0 -0
  128. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/msgpack.py +0 -0
  129. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/mustache.py +0 -0
  130. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/nc.py +0 -0
  131. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ndjson.py +0 -0
  132. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/numbers.py +0 -0
  133. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/ods.py +0 -0
  134. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/orc.py +0 -0
  135. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/parquet.py +0 -0
  136. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/pb.py +0 -0
  137. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/pbf.py +0 -0
  138. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/properties.py +0 -0
  139. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/proto.py +0 -0
  140. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/psv.py +0 -0
  141. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/rda.py +0 -0
  142. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/rds.py +0 -0
  143. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sas7bdat.py +0 -0
  144. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sav.py +0 -0
  145. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sqlite.py +0 -0
  146. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/stub.py +0 -0
  147. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/sylk.py +0 -0
  148. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/tab.py +0 -0
  149. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/toml.py +0 -0
  150. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/tsv.py +0 -0
  151. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/txt.py +0 -0
  152. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/vm.py +0 -0
  153. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/wks.py +0 -0
  154. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xls.py +0 -0
  155. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xlsm.py +0 -0
  156. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xlsx.py +0 -0
  157. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xml.py +0 -0
  158. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/xpt.py +0 -0
  159. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/yaml.py +0 -0
  160. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/zip.py +0 -0
  161. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/file/zsav.py +0 -0
  162. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/mixins.py +0 -0
  163. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/README.md +0 -0
  164. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/__init__.py +0 -0
  165. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/transform.py +0 -0
  166. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/ops/validate.py +0 -0
  167. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/py.typed +0 -0
  168. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/__init__.py +0 -0
  169. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/ddl.sql.j2 +0 -0
  170. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/templates/view.sql.j2 +0 -0
  171. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/types.py +0 -0
  172. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus/utils.py +0 -0
  173. {etlplus-0.14.1/etlplus/config → etlplus-0.15.0/etlplus/workflow}/utils.py +0 -0
  174. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/dependency_links.txt +0 -0
  175. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/entry_points.txt +0 -0
  176. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/requires.txt +0 -0
  177. {etlplus-0.14.1 → etlplus-0.15.0}/etlplus.egg-info/top_level.txt +0 -0
  178. {etlplus-0.14.1 → etlplus-0.15.0}/examples/README.md +0 -0
  179. {etlplus-0.14.1 → etlplus-0.15.0}/examples/configs/ddl_spec.yml +0 -0
  180. {etlplus-0.14.1 → etlplus-0.15.0}/examples/configs/pipeline.yml +0 -0
  181. {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.csv +0 -0
  182. {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.json +0 -0
  183. {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.xml +0 -0
  184. {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.xsd +0 -0
  185. {etlplus-0.14.1 → etlplus-0.15.0}/examples/data/sample.yaml +0 -0
  186. {etlplus-0.14.1 → etlplus-0.15.0}/examples/quickstart_python.py +0 -0
  187. {etlplus-0.14.1 → etlplus-0.15.0}/pyproject.toml +0 -0
  188. {etlplus-0.14.1 → etlplus-0.15.0}/pytest.ini +0 -0
  189. {etlplus-0.14.1 → etlplus-0.15.0}/setup.cfg +0 -0
  190. {etlplus-0.14.1 → etlplus-0.15.0}/setup.py +0 -0
  191. {etlplus-0.14.1 → etlplus-0.15.0}/tests/__init__.py +0 -0
  192. {etlplus-0.14.1 → etlplus-0.15.0}/tests/conftest.py +0 -0
  193. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_cli.py +0 -0
  194. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_examples_data_parity.py +0 -0
  195. {etlplus-0.14.1 → etlplus-0.15.0}/tests/integration/test_i_pipeline_smoke.py +0 -0
  196. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/conftest.py +0 -0
  197. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_api_utils.py +0 -0
  198. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_auth.py +0 -0
  199. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_config.py +0 -0
  200. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_endpoint_client.py +0 -0
  201. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_mocks.py +0 -0
  202. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_pagination_client.py +0 -0
  203. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_pagination_config.py +0 -0
  204. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_paginator.py +0 -0
  205. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_rate_limit_config.py +0 -0
  206. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_rate_limiter.py +0 -0
  207. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_request_manager.py +0 -0
  208. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_retry_manager.py +0 -0
  209. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_transport.py +0 -0
  210. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/api/test_u_types.py +0 -0
  211. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_io.py +0 -0
  212. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_main.py +0 -0
  213. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/cli/test_u_cli_state.py +0 -0
  214. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_ddl.py +0 -0
  215. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_engine.py +0 -0
  216. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_orm.py +0 -0
  217. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/database/test_u_database_schema.py +0 -0
  218. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/file/test_u_file_core.py +0 -0
  219. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/file/test_u_file_enums.py +0 -0
  220. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/file/test_u_file_yaml.py +0 -0
  221. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_extract.py +0 -0
  222. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_run.py +0 -0
  223. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_transform.py +0 -0
  224. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_utils.py +0 -0
  225. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/ops/test_u_ops_validate.py +0 -0
  226. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_main.py +0 -0
  227. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_mixins.py +0 -0
  228. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_utils.py +0 -0
  229. {etlplus-0.14.1 → etlplus-0.15.0}/tests/unit/test_u_version.py +0 -0
  230. {etlplus-0.14.1 → etlplus-0.15.0}/tools/update_demo_snippets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.14.1
3
+ Version: 0.15.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
@@ -1,4 +1,4 @@
1
- # etlplus package
1
+ # `etlplus` Package
2
2
 
3
3
  The `etlplus` package provides a unified Python API and CLI for ETL operations: extraction,
4
4
  validation, transformation, and loading of data from files, APIs, and databases.
@@ -1,4 +1,4 @@
1
- # etlplus.api subpackage
1
+ # `etlplus.api` Subpackage
2
2
 
3
3
  Documentation for the `etlplus.api` subpackage: a lightweight HTTP client and helpers for paginated
4
4
  REST endpoints.
@@ -12,7 +12,7 @@ REST endpoints.
12
12
 
13
13
  Back to project overview: see the top-level [README](../../README.md).
14
14
 
15
- - [etlplus.api subpackage](#etlplusapi-subpackage)
15
+ - [`etlplus.api` Subpackage](#etlplusapi-subpackage)
16
16
  - [Installation](#installation)
17
17
  - [Quickstart](#quickstart)
18
18
  - [Overriding Rate Limits Per Call](#overriding-rate-limits-per-call)
@@ -78,6 +78,7 @@ from .config import ApiConfig
78
78
  from .config import ApiProfileConfig
79
79
  from .config import EndpointConfig
80
80
  from .endpoint_client import EndpointClient
81
+ from .enums import HttpMethod
81
82
  from .pagination import CursorPaginationConfigMap
82
83
  from .pagination import PagePaginationConfigMap
83
84
  from .pagination import PaginationClient
@@ -123,6 +124,7 @@ __all__ = [
123
124
  'RequestOptions',
124
125
  'RetryStrategy',
125
126
  # Enums
127
+ 'HttpMethod',
126
128
  'PaginationType',
127
129
  # Functions
128
130
  'build_http_adapter',
@@ -18,6 +18,7 @@ Notes
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
+ from collections.abc import Callable
21
22
  from collections.abc import Mapping
22
23
  from dataclasses import dataclass
23
24
  from dataclasses import field
@@ -29,13 +30,13 @@ from typing import overload
29
30
  from urllib.parse import urlsplit
30
31
  from urllib.parse import urlunsplit
31
32
 
32
- from ..enums import HttpMethod
33
33
  from ..types import StrAnyMap
34
34
  from ..types import StrStrMap
35
35
  from ..utils import cast_str_dict
36
36
  from ..utils import coerce_dict
37
37
  from ..utils import maybe_mapping
38
38
  from .endpoint_client import EndpointClient
39
+ from .enums import HttpMethod
39
40
  from .pagination import PaginationConfig
40
41
  from .rate_limiting import RateLimitConfig
41
42
 
@@ -106,6 +107,33 @@ def _effective_service_defaults(
106
107
  return fallback_base, fallback_headers
107
108
 
108
109
 
110
+ def _freeze_mapping(
111
+ mapping: Mapping[Any, Any],
112
+ *,
113
+ key_cast: Callable[[Any], Any] | None = None,
114
+ ) -> MappingProxyType:
115
+ """
116
+ Return an immutable copy of a mapping, optionally normalizing keys.
117
+
118
+ Parameters
119
+ ----------
120
+ mapping : Mapping[Any, Any]
121
+ Source mapping to freeze.
122
+ key_cast : Callable[[Any], Any] | None, optional
123
+ Optional key coercion applied to each key.
124
+
125
+ Returns
126
+ -------
127
+ MappingProxyType
128
+ Read-only mapping proxy with normalized keys.
129
+ """
130
+ if key_cast is None:
131
+ data = dict(mapping)
132
+ else:
133
+ data = {key_cast(key): value for key, value in mapping.items()}
134
+ return MappingProxyType(data)
135
+
136
+
109
137
  def _normalize_method(
110
138
  value: Any,
111
139
  ) -> Any | None:
@@ -232,16 +260,8 @@ class ApiProfileConfig:
232
260
  # -- Magic Methods (Object Lifecycle) -- #
233
261
 
234
262
  def __post_init__(self) -> None:
235
- object.__setattr__(
236
- self,
237
- 'headers',
238
- MappingProxyType(dict(self.headers)),
239
- )
240
- object.__setattr__(
241
- self,
242
- 'auth',
243
- MappingProxyType(dict(self.auth)),
244
- )
263
+ object.__setattr__(self, 'headers', _freeze_mapping(self.headers))
264
+ object.__setattr__(self, 'auth', _freeze_mapping(self.auth))
245
265
 
246
266
  # -- Class Methods -- #
247
267
 
@@ -340,20 +360,16 @@ class ApiConfig:
340
360
  # -- Magic Methods (Object Lifecycle) -- #
341
361
 
342
362
  def __post_init__(self) -> None:
343
- object.__setattr__(
344
- self,
345
- 'headers',
346
- MappingProxyType(dict(self.headers)),
347
- )
363
+ object.__setattr__(self, 'headers', _freeze_mapping(self.headers))
348
364
  object.__setattr__(
349
365
  self,
350
366
  'endpoints',
351
- MappingProxyType({str(k): v for k, v in self.endpoints.items()}),
367
+ _freeze_mapping(self.endpoints, key_cast=str),
352
368
  )
353
369
  object.__setattr__(
354
370
  self,
355
371
  'profiles',
356
- MappingProxyType({str(k): v for k, v in self.profiles.items()}),
372
+ _freeze_mapping(self.profiles, key_cast=str),
357
373
  )
358
374
 
359
375
  # -- Internal Instance Methods -- #
@@ -545,12 +561,12 @@ class EndpointConfig:
545
561
  object.__setattr__(
546
562
  self,
547
563
  'path_params',
548
- MappingProxyType(dict(self.path_params)),
564
+ _freeze_mapping(self.path_params),
549
565
  )
550
566
  object.__setattr__(
551
567
  self,
552
568
  'query_params',
553
- MappingProxyType(dict(self.query_params)),
569
+ _freeze_mapping(self.query_params),
554
570
  )
555
571
 
556
572
  # -- Class Methods -- #
@@ -0,0 +1,51 @@
1
+ """
2
+ :mod:`etlplus.api.enums` module.
3
+
4
+ File-specific REST API-aligned enums and helpers.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from ..enums import CoercibleStrEnum
10
+
11
+ # SECTION: EXPORTS ========================================================= #
12
+
13
+
14
+ __all__ = [
15
+ # Enums
16
+ 'HttpMethod',
17
+ ]
18
+
19
+
20
+ # SECTION: ENUMS ============================================================ #
21
+
22
+
23
+ class HttpMethod(CoercibleStrEnum):
24
+ """Supported HTTP verbs that accept JSON payloads."""
25
+
26
+ # -- Constants -- #
27
+
28
+ CONNECT = 'connect'
29
+ DELETE = 'delete'
30
+ GET = 'get'
31
+ HEAD = 'head'
32
+ OPTIONS = 'options'
33
+ PATCH = 'patch'
34
+ POST = 'post'
35
+ PUT = 'put'
36
+ TRACE = 'trace'
37
+
38
+ # -- Getters -- #
39
+
40
+ @property
41
+ def allows_body(self) -> bool:
42
+ """
43
+ Whether the method typically allows a request body.
44
+
45
+ Notes
46
+ -----
47
+ - RFCs do not strictly forbid bodies on some other methods (e.g.,
48
+ ``DELETE``), but many servers/clients do not expect them. We mark
49
+ ``POST``, ``PUT``, and ``PATCH`` as True.
50
+ """
51
+ return self in {HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH}
@@ -1,5 +1,5 @@
1
1
  """
2
- :mod:`etlplus.api.client` module.
2
+ :mod:`etlplus.api.pagination.client` module.
3
3
 
4
4
  Client-facing pagination driver for REST API responses.
5
5
 
@@ -1,5 +1,5 @@
1
1
  """
2
- :mod:`etlplus.api.rate_limiting.rate_limiter` module.
2
+ :mod:`etlplus.api.rate_limiting.config` module.
3
3
 
4
4
  Rate limiting configuration primitives.
5
5
 
@@ -268,6 +268,18 @@ class RateLimitConfig(BoundsWarningsMixin):
268
268
  ) -> Self:
269
269
  """
270
270
  Normalize rate-limit config and overrides into a single instance.
271
+
272
+ Parameters
273
+ ----------
274
+ rate_limit : StrAnyMap | RateLimitConfig | None, optional
275
+ Base rate-limit configuration to normalize.
276
+ overrides : RateLimitOverrides, optional
277
+ Override values that take precedence over ``rate_limit``.
278
+
279
+ Returns
280
+ -------
281
+ Self
282
+ Normalized rate-limit configuration.
271
283
  """
272
284
  normalized = _coerce_rate_limit_map(rate_limit)
273
285
  cfg = _merge_rate_limit(normalized, overrides)
@@ -20,6 +20,7 @@ from __future__ import annotations
20
20
 
21
21
  import time
22
22
  from dataclasses import dataclass
23
+ from typing import Self
23
24
 
24
25
  from ...utils import to_float
25
26
  from ...utils import to_positive_float
@@ -143,13 +144,13 @@ class RateLimiter:
143
144
  # -- Class Methods -- #
144
145
 
145
146
  @classmethod
146
- def disabled(cls) -> RateLimiter:
147
+ def disabled(cls) -> Self:
147
148
  """
148
149
  Create a limiter that never sleeps.
149
150
 
150
151
  Returns
151
152
  -------
152
- RateLimiter
153
+ Self
153
154
  Instance with rate limiting disabled.
154
155
  """
155
156
  return cls(sleep_seconds=0.0)
@@ -158,7 +159,7 @@ class RateLimiter:
158
159
  def fixed(
159
160
  cls,
160
161
  seconds: float,
161
- ) -> RateLimiter:
162
+ ) -> Self:
162
163
  """
163
164
  Create a limiter with a fixed non-negative delay.
164
165
 
@@ -170,7 +171,7 @@ class RateLimiter:
170
171
 
171
172
  Returns
172
173
  -------
173
- RateLimiter
174
+ Self
174
175
  Instance with the specified delay.
175
176
  """
176
177
  value = to_float(seconds, 0.0, minimum=0.0) or 0.0
@@ -181,7 +182,7 @@ class RateLimiter:
181
182
  def from_config(
182
183
  cls,
183
184
  cfg: RateLimitInput,
184
- ) -> RateLimiter:
185
+ ) -> Self:
185
186
  """
186
187
  Build a :class:`RateLimiter` from a configuration mapping.
187
188
 
@@ -201,12 +202,10 @@ class RateLimiter:
201
202
 
202
203
  Returns
203
204
  -------
204
- RateLimiter
205
+ Self
205
206
  Instance with normalized ``sleep_seconds`` and ``max_per_sec``.
206
207
  """
207
208
  config = RateLimitConfig.from_inputs(rate_limit=cfg)
208
- if config is None:
209
- return cls.disabled()
210
209
 
211
210
  # RateLimiter.__post_init__ will normalize and enforce invariants.
212
211
  return cls(**config.as_mapping())
@@ -261,6 +260,4 @@ class RateLimiter:
261
260
  rate_limit=rate_limit,
262
261
  overrides=overrides,
263
262
  )
264
- if config is None or not config.sleep_seconds:
265
- return 0.0
266
- return float(config.sleep_seconds)
263
+ return float(config.sleep_seconds) if config.sleep_seconds else 0.0
@@ -14,6 +14,7 @@ from collections.abc import Sequence
14
14
  from dataclasses import dataclass
15
15
  from dataclasses import field
16
16
  from functools import partial
17
+ from types import TracebackType
17
18
  from typing import Any
18
19
  from typing import cast
19
20
 
@@ -137,7 +138,7 @@ class RequestManager:
137
138
  self,
138
139
  exc_type: type[BaseException] | None,
139
140
  exc: BaseException | None,
140
- tb: Any,
141
+ tb: TracebackType | None,
141
142
  ) -> None:
142
143
  """
143
144
  Exit the runtime context and close owned sessions.
@@ -148,7 +149,7 @@ class RequestManager:
148
149
  Exception type if raised, else ``None``.
149
150
  exc : BaseException | None
150
151
  Exception instance if raised, else ``None``.
151
- tb : Any
152
+ tb : TracebackType | None
152
153
  Traceback if an exception was raised, else ``None``.
153
154
  """
154
155
  if self._ctx_session is None:
@@ -275,7 +276,7 @@ class RequestManager:
275
276
 
276
277
  try:
277
278
  policy = self.retry
278
- if not policy:
279
+ if policy is None:
279
280
  try:
280
281
  return fetch(url, **call_kwargs)
281
282
  except requests.RequestException as exc: # pragma: no cover
@@ -438,9 +439,13 @@ class RequestManager:
438
439
  if isinstance(payload, dict):
439
440
  return cast(JSONDict, payload)
440
441
  if isinstance(payload, list):
441
- if all(isinstance(item, dict) for item in payload):
442
- return cast(JSONData, payload)
443
- return [{'value': item} for item in payload]
442
+ out: list[JSONDict] = []
443
+ for item in payload:
444
+ if isinstance(item, dict):
445
+ out.append(cast(JSONDict, item))
446
+ else:
447
+ out.append({'value': item})
448
+ return cast(JSONData, out)
444
449
  return {'value': payload}
445
450
  return {
446
451
  'content': response.text,
@@ -191,7 +191,19 @@ def _build_retry_value(
191
191
  def _normalize_retry_kwargs(
192
192
  retries_cfg: Mapping[str, Any],
193
193
  ) -> dict[str, Any]:
194
- """Filter and normalize urllib3 ``Retry`` kwargs from a mapping."""
194
+ """
195
+ Filter and normalize urllib3 ``Retry`` kwargs from a mapping.
196
+
197
+ Parameters
198
+ ----------
199
+ retries_cfg : Mapping[str, Any]
200
+ Raw retry configuration mapping.
201
+
202
+ Returns
203
+ -------
204
+ dict[str, Any]
205
+ Filtered and normalized keyword arguments for ``Retry``.
206
+ """
195
207
  allowed_keys = {
196
208
  'total',
197
209
  'connect',
@@ -239,7 +251,7 @@ def _resolve_max_retries(
239
251
  """
240
252
  match retries_cfg:
241
253
  case int():
242
- return retries_cfg
254
+ return to_maximum_int(retries_cfg, 0)
243
255
  case Mapping():
244
256
  try:
245
257
  return _build_retry_value(retries_cfg)
@@ -22,6 +22,7 @@ from __future__ import annotations
22
22
  from collections.abc import Callable
23
23
  from dataclasses import dataclass
24
24
  from typing import Any
25
+ from typing import Self
25
26
  from typing import cast
26
27
 
27
28
  from ..types import JSONData
@@ -75,9 +76,9 @@ class RequestOptions:
75
76
  # -- Magic Methods (Object Lifecycle) -- #
76
77
 
77
78
  def __post_init__(self) -> None:
78
- if self.params:
79
+ if self.params is not None:
79
80
  object.__setattr__(self, 'params', dict(self.params))
80
- if self.headers:
81
+ if self.headers is not None:
81
82
  object.__setattr__(self, 'headers', dict(self.headers))
82
83
 
83
84
  # -- Instance Methods -- #
@@ -92,9 +93,9 @@ class RequestOptions:
92
93
  Keyword arguments for ``requests`` methods.
93
94
  """
94
95
  kw: dict[str, Any] = {}
95
- if self.params:
96
+ if self.params is not None:
96
97
  kw['params'] = dict(self.params)
97
- if self.headers:
98
+ if self.headers is not None:
98
99
  kw['headers'] = dict(self.headers)
99
100
  if self.timeout is not None:
100
101
  kw['timeout'] = self.timeout
@@ -106,7 +107,7 @@ class RequestOptions:
106
107
  params: Params | None | object = _UNSET,
107
108
  headers: Headers | None | object = _UNSET,
108
109
  timeout: float | None | object = _UNSET,
109
- ) -> RequestOptions:
110
+ ) -> Self:
110
111
  """
111
112
  Return a copy with the provided fields replaced.
112
113
 
@@ -146,7 +147,7 @@ class RequestOptions:
146
147
  else:
147
148
  next_timeout = cast(float | None, timeout)
148
149
 
149
- return RequestOptions(
150
+ return self.__class__(
150
151
  params=next_params,
151
152
  headers=next_headers,
152
153
  timeout=next_timeout,