etlplus 0.14.1__tar.gz → 0.15.4__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 (228) hide show
  1. {etlplus-0.14.1/etlplus.egg-info → etlplus-0.15.4}/PKG-INFO +4 -4
  2. {etlplus-0.14.1 → etlplus-0.15.4}/README.md +3 -3
  3. {etlplus-0.14.1 → etlplus-0.15.4}/docs/pipeline-guide.md +2 -2
  4. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/README.md +4 -4
  5. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/README.md +33 -2
  6. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/__init__.py +2 -0
  7. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/config.py +39 -28
  8. etlplus-0.15.4/etlplus/api/enums.py +51 -0
  9. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/pagination/client.py +1 -1
  10. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/rate_limiting/config.py +13 -1
  11. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/rate_limiting/rate_limiter.py +8 -11
  12. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/request_manager.py +11 -6
  13. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/transport.py +14 -2
  14. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/types.py +96 -6
  15. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/utils.py +92 -78
  16. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/README.md +2 -2
  17. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/commands.py +75 -42
  18. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/handlers.py +32 -14
  19. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/main.py +1 -1
  20. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/state.py +4 -7
  21. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/database/README.md +2 -2
  22. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/database/engine.py +18 -2
  23. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/database/orm.py +2 -0
  24. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/enums.py +0 -32
  25. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/README.md +2 -2
  26. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/_io.py +39 -0
  27. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/json.py +2 -14
  28. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/yaml.py +2 -14
  29. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/extract.py +1 -1
  30. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/load.py +1 -1
  31. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/run.py +15 -10
  32. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/utils.py +32 -43
  33. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/validate.py +3 -3
  34. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/templates/README.md +2 -2
  35. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/types.py +3 -2
  36. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/utils.py +136 -2
  37. {etlplus-0.14.1/etlplus/config → etlplus-0.15.4/etlplus/workflow}/README.md +6 -6
  38. {etlplus-0.14.1/etlplus/config → etlplus-0.15.4/etlplus/workflow}/__init__.py +10 -23
  39. {etlplus-0.14.1/etlplus/config → etlplus-0.15.4/etlplus/workflow}/connector.py +58 -44
  40. {etlplus-0.14.1/etlplus → etlplus-0.15.4/etlplus/workflow}/dag.py +6 -4
  41. {etlplus-0.14.1/etlplus/config → etlplus-0.15.4/etlplus/workflow}/jobs.py +101 -38
  42. {etlplus-0.14.1/etlplus/config → etlplus-0.15.4/etlplus/workflow}/pipeline.py +57 -49
  43. {etlplus-0.14.1/etlplus/config → etlplus-0.15.4/etlplus/workflow}/profile.py +8 -5
  44. etlplus-0.15.4/etlplus/workflow/types.py +115 -0
  45. {etlplus-0.14.1 → etlplus-0.15.4/etlplus.egg-info}/PKG-INFO +4 -4
  46. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus.egg-info/SOURCES.txt +13 -13
  47. {etlplus-0.14.1 → etlplus-0.15.4}/tests/__init__.py +2 -0
  48. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/conftest.py +6 -6
  49. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_examples_data_parity.py +12 -11
  50. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_pagination_strategy.py +11 -11
  51. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_pipeline_yaml_load.py +1 -1
  52. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_run.py +1 -1
  53. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_run_profile_pagination_defaults.py +1 -1
  54. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_run_profile_rate_limit_defaults.py +1 -1
  55. etlplus-0.15.4/tests/unit/api/test_u_api_enums.py +34 -0
  56. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_types.py +79 -47
  57. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/cli/conftest.py +2 -2
  58. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/cli/test_u_cli_handlers.py +3 -1
  59. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/cli/test_u_cli_main.py +1 -0
  60. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/conftest.py +15 -13
  61. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/file/test_u_file_yaml.py +1 -3
  62. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/ops/test_u_ops_extract.py +4 -11
  63. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/ops/test_u_ops_load.py +8 -14
  64. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/ops/test_u_ops_transform.py +3 -1
  65. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/ops/test_u_ops_validate.py +9 -9
  66. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/test_u_enums.py +9 -21
  67. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/test_u_main.py +5 -3
  68. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/test_u_utils.py +110 -4
  69. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/test_u_version.py +23 -15
  70. etlplus-0.14.1/tests/unit/config/test_u_connector.py → etlplus-0.15.4/tests/unit/workflow/test_u_workflow_connector.py +6 -6
  71. etlplus-0.15.4/tests/unit/workflow/test_u_workflow_jobs.py +143 -0
  72. etlplus-0.14.1/tests/unit/config/test_u_pipeline.py → etlplus-0.15.4/tests/unit/workflow/test_u_workflow_pipeline.py +38 -34
  73. etlplus-0.14.1/etlplus/config/types.py +0 -204
  74. etlplus-0.14.1/etlplus/config/utils.py +0 -120
  75. etlplus-0.14.1/tests/unit/config/test_u_config_utils.py +0 -129
  76. etlplus-0.14.1/tests/unit/config/test_u_jobs.py +0 -131
  77. {etlplus-0.14.1 → etlplus-0.15.4}/.coveragerc +0 -0
  78. {etlplus-0.14.1 → etlplus-0.15.4}/.editorconfig +0 -0
  79. {etlplus-0.14.1 → etlplus-0.15.4}/.gitattributes +0 -0
  80. {etlplus-0.14.1 → etlplus-0.15.4}/.github/actions/python-bootstrap/action.yml +0 -0
  81. {etlplus-0.14.1 → etlplus-0.15.4}/.github/workflows/ci.yml +0 -0
  82. {etlplus-0.14.1 → etlplus-0.15.4}/.gitignore +0 -0
  83. {etlplus-0.14.1 → etlplus-0.15.4}/.pre-commit-config.yaml +0 -0
  84. {etlplus-0.14.1 → etlplus-0.15.4}/.ruff.toml +0 -0
  85. {etlplus-0.14.1 → etlplus-0.15.4}/CODE_OF_CONDUCT.md +0 -0
  86. {etlplus-0.14.1 → etlplus-0.15.4}/CONTRIBUTING.md +0 -0
  87. {etlplus-0.14.1 → etlplus-0.15.4}/DEMO.md +0 -0
  88. {etlplus-0.14.1 → etlplus-0.15.4}/LICENSE +0 -0
  89. {etlplus-0.14.1 → etlplus-0.15.4}/MANIFEST.in +0 -0
  90. {etlplus-0.14.1 → etlplus-0.15.4}/Makefile +0 -0
  91. {etlplus-0.14.1 → etlplus-0.15.4}/REFERENCES.md +0 -0
  92. {etlplus-0.14.1 → etlplus-0.15.4}/SECURITY.md +0 -0
  93. {etlplus-0.14.1 → etlplus-0.15.4}/SUPPORT.md +0 -0
  94. {etlplus-0.14.1 → etlplus-0.15.4}/docs/README.md +0 -0
  95. {etlplus-0.14.1 → etlplus-0.15.4}/docs/snippets/installation_version.md +0 -0
  96. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/__init__.py +0 -0
  97. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/__main__.py +0 -0
  98. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/__version__.py +0 -0
  99. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/auth.py +0 -0
  100. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/endpoint_client.py +0 -0
  101. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/errors.py +0 -0
  102. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/pagination/__init__.py +0 -0
  103. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/pagination/config.py +0 -0
  104. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/pagination/paginator.py +0 -0
  105. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/rate_limiting/__init__.py +0 -0
  106. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/api/retry_manager.py +0 -0
  107. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/__init__.py +0 -0
  108. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/constants.py +0 -0
  109. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/io.py +0 -0
  110. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/options.py +0 -0
  111. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/cli/types.py +0 -0
  112. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/database/__init__.py +0 -0
  113. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/database/ddl.py +0 -0
  114. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/database/schema.py +0 -0
  115. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/database/types.py +0 -0
  116. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/__init__.py +0 -0
  117. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/_imports.py +0 -0
  118. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/accdb.py +0 -0
  119. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/arrow.py +0 -0
  120. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/avro.py +0 -0
  121. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/bson.py +0 -0
  122. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/cbor.py +0 -0
  123. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/cfg.py +0 -0
  124. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/conf.py +0 -0
  125. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/core.py +0 -0
  126. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/csv.py +0 -0
  127. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/dat.py +0 -0
  128. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/dta.py +0 -0
  129. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/duckdb.py +0 -0
  130. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/enums.py +0 -0
  131. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/feather.py +0 -0
  132. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/fwf.py +0 -0
  133. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/gz.py +0 -0
  134. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/hbs.py +0 -0
  135. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/hdf5.py +0 -0
  136. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/ini.py +0 -0
  137. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/ion.py +0 -0
  138. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/jinja2.py +0 -0
  139. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/log.py +0 -0
  140. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/mat.py +0 -0
  141. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/mdb.py +0 -0
  142. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/msgpack.py +0 -0
  143. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/mustache.py +0 -0
  144. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/nc.py +0 -0
  145. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/ndjson.py +0 -0
  146. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/numbers.py +0 -0
  147. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/ods.py +0 -0
  148. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/orc.py +0 -0
  149. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/parquet.py +0 -0
  150. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/pb.py +0 -0
  151. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/pbf.py +0 -0
  152. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/properties.py +0 -0
  153. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/proto.py +0 -0
  154. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/psv.py +0 -0
  155. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/rda.py +0 -0
  156. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/rds.py +0 -0
  157. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/sas7bdat.py +0 -0
  158. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/sav.py +0 -0
  159. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/sqlite.py +0 -0
  160. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/stub.py +0 -0
  161. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/sylk.py +0 -0
  162. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/tab.py +0 -0
  163. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/toml.py +0 -0
  164. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/tsv.py +0 -0
  165. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/txt.py +0 -0
  166. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/vm.py +0 -0
  167. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/wks.py +0 -0
  168. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/xls.py +0 -0
  169. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/xlsm.py +0 -0
  170. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/xlsx.py +0 -0
  171. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/xml.py +0 -0
  172. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/xpt.py +0 -0
  173. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/zip.py +0 -0
  174. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/file/zsav.py +0 -0
  175. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/mixins.py +0 -0
  176. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/README.md +0 -0
  177. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/__init__.py +0 -0
  178. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/ops/transform.py +0 -0
  179. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/py.typed +0 -0
  180. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/templates/__init__.py +0 -0
  181. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/templates/ddl.sql.j2 +0 -0
  182. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus/templates/view.sql.j2 +0 -0
  183. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus.egg-info/dependency_links.txt +0 -0
  184. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus.egg-info/entry_points.txt +0 -0
  185. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus.egg-info/requires.txt +0 -0
  186. {etlplus-0.14.1 → etlplus-0.15.4}/etlplus.egg-info/top_level.txt +0 -0
  187. {etlplus-0.14.1 → etlplus-0.15.4}/examples/README.md +0 -0
  188. {etlplus-0.14.1 → etlplus-0.15.4}/examples/configs/ddl_spec.yml +0 -0
  189. {etlplus-0.14.1 → etlplus-0.15.4}/examples/configs/pipeline.yml +0 -0
  190. {etlplus-0.14.1 → etlplus-0.15.4}/examples/data/sample.csv +0 -0
  191. {etlplus-0.14.1 → etlplus-0.15.4}/examples/data/sample.json +0 -0
  192. {etlplus-0.14.1 → etlplus-0.15.4}/examples/data/sample.xml +0 -0
  193. {etlplus-0.14.1 → etlplus-0.15.4}/examples/data/sample.xsd +0 -0
  194. {etlplus-0.14.1 → etlplus-0.15.4}/examples/data/sample.yaml +0 -0
  195. {etlplus-0.14.1 → etlplus-0.15.4}/examples/quickstart_python.py +0 -0
  196. {etlplus-0.14.1 → etlplus-0.15.4}/pyproject.toml +0 -0
  197. {etlplus-0.14.1 → etlplus-0.15.4}/pytest.ini +0 -0
  198. {etlplus-0.14.1 → etlplus-0.15.4}/setup.cfg +0 -0
  199. {etlplus-0.14.1 → etlplus-0.15.4}/setup.py +0 -0
  200. {etlplus-0.14.1 → etlplus-0.15.4}/tests/conftest.py +0 -0
  201. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_cli.py +0 -0
  202. {etlplus-0.14.1 → etlplus-0.15.4}/tests/integration/test_i_pipeline_smoke.py +0 -0
  203. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/conftest.py +0 -0
  204. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_api_utils.py +0 -0
  205. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_auth.py +0 -0
  206. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_config.py +0 -0
  207. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_endpoint_client.py +0 -0
  208. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_mocks.py +0 -0
  209. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_pagination_client.py +0 -0
  210. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_pagination_config.py +0 -0
  211. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_paginator.py +0 -0
  212. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_rate_limit_config.py +0 -0
  213. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_rate_limiter.py +0 -0
  214. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_request_manager.py +0 -0
  215. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_retry_manager.py +0 -0
  216. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/api/test_u_transport.py +0 -0
  217. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/cli/test_u_cli_io.py +0 -0
  218. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/cli/test_u_cli_state.py +0 -0
  219. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/database/test_u_database_ddl.py +0 -0
  220. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/database/test_u_database_engine.py +0 -0
  221. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/database/test_u_database_orm.py +0 -0
  222. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/database/test_u_database_schema.py +0 -0
  223. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/file/test_u_file_core.py +0 -0
  224. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/file/test_u_file_enums.py +0 -0
  225. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/ops/test_u_ops_run.py +0 -0
  226. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/ops/test_u_ops_utils.py +0 -0
  227. {etlplus-0.14.1 → etlplus-0.15.4}/tests/unit/test_u_mixins.py +0 -0
  228. {etlplus-0.14.1 → etlplus-0.15.4}/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.4
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
@@ -805,12 +805,12 @@ Navigate to detailed documentation for each subpackage:
805
805
 
806
806
  - [etlplus.api](etlplus/api/README.md): Lightweight HTTP client and paginated REST helpers
807
807
  - [etlplus.file](etlplus/file/README.md): Unified file format support and helpers
808
- - [etlplus.config](etlplus/config/README.md): Configuration helpers for connectors, pipelines, jobs,
809
- and profiles
810
- - [etlplus.cli](etlplus/cli/README.md): Command-line interface for ETLPlus workflows
808
+ - [etlplus.cli](etlplus/cli/README.md): Command-line interface definitions for `etlplus`
811
809
  - [etlplus.database](etlplus/database/README.md): Database engine, schema, and ORM helpers
812
810
  - [etlplus.templates](etlplus/templates/README.md): SQL and DDL template helpers
813
811
  - [etlplus.validation](etlplus/validation/README.md): Data validation utilities and helpers
812
+ - [etlplus.workflow](etlplus/workflow/README.md): Helpers for data connectors, pipelines, jobs, and
813
+ profiles
814
814
 
815
815
  ### Community Health
816
816
 
@@ -755,12 +755,12 @@ Navigate to detailed documentation for each subpackage:
755
755
 
756
756
  - [etlplus.api](etlplus/api/README.md): Lightweight HTTP client and paginated REST helpers
757
757
  - [etlplus.file](etlplus/file/README.md): Unified file format support and helpers
758
- - [etlplus.config](etlplus/config/README.md): Configuration helpers for connectors, pipelines, jobs,
759
- and profiles
760
- - [etlplus.cli](etlplus/cli/README.md): Command-line interface for ETLPlus workflows
758
+ - [etlplus.cli](etlplus/cli/README.md): Command-line interface definitions for `etlplus`
761
759
  - [etlplus.database](etlplus/database/README.md): Database engine, schema, and ORM helpers
762
760
  - [etlplus.templates](etlplus/templates/README.md): SQL and DDL template helpers
763
761
  - [etlplus.validation](etlplus/validation/README.md): Data validation utilities and helpers
762
+ - [etlplus.workflow](etlplus/workflow/README.md): Helpers for data connectors, pipelines, jobs, and
763
+ profiles
764
764
 
765
765
  ### Community Health
766
766
 
@@ -377,9 +377,9 @@ Details:
377
377
  - Unknown or malformed entries are skipped rather than failing the whole load (keeping pipeline
378
378
  authoring permissive).
379
379
  - The connector kind is also available as a type-safe literal in code as
380
- `etlplus.config.ConnectorType` (values: `"file" | "database" | "api"`).
380
+ `etlplus.workflow.ConnectorType` (values: `"file" | "database" | "api"`).
381
381
 
382
- To add new connector kinds in the future, implement a new dataclass in `etlplus.config.connector`
382
+ To add new connector kinds in the future, implement a new dataclass in `etlplus.workflow.connector`
383
383
  and extend the internal parser to handle its `type` value.
384
384
 
385
385
  ## Jobs
@@ -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.
@@ -13,12 +13,12 @@ Back to project overview: see the top-level [README](../README.md).
13
13
 
14
14
  - [etlplus.api](api/README.md): Lightweight HTTP client and paginated REST helpers
15
15
  - [etlplus.file](file/README.md): Unified file format support and helpers
16
- - [etlplus.config](config/README.md): Configuration helpers for connectors, pipelines, jobs, and
17
- profiles
18
- - [etlplus.cli](cli/README.md): Command-line interface for ETLPlus workflows
16
+ - [etlplus.cli](cli/README.md): Command-line interface definitions for `etlplus`
19
17
  - [etlplus.database](database/README.md): Database engine, schema, and ORM helpers
20
18
  - [etlplus.templates](templates/README.md): SQL and DDL template helpers
21
19
  - [etlplus.validation](validation/README.md): Data validation utilities and helpers
20
+ - [etlplus.workflow](etlplus/workflow/README.md): Helpers for data connectors, pipelines, jobs, and
21
+ profiles
22
22
 
23
23
  ## Quickstart
24
24
 
@@ -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)
@@ -22,6 +22,7 @@ Back to project overview: see the top-level [README](../../README.md).
22
22
  - [Authentication](#authentication)
23
23
  - [Errors and Rate Limiting](#errors-and-rate-limiting)
24
24
  - [Types and Transport](#types-and-transport)
25
+ - [Config Schemas](#config-schemas)
25
26
  - [Supporting Modules](#supporting-modules)
26
27
  - [Minimal Contract](#minimal-contract)
27
28
  - [See also](#see-also)
@@ -225,6 +226,36 @@ providers can fall back to their own defaults. If you already possess a static t
225
226
  `etlplus/api/request_manager.py` wraps `requests` sessions plus retry orchestration. Advanced
226
227
  users may consult those modules to adapt behavior.
227
228
 
229
+ ## Config Schemas
230
+
231
+ `etlplus.api.types` defines TypedDict-based configuration shapes for API profiles and endpoints.
232
+ Runtime parsing remains permissive in `etlplus.api.config`, but these types improve IDE
233
+ autocomplete and static analysis.
234
+
235
+ Exported types:
236
+
237
+ - `ApiConfigMap`: top-level API config shape
238
+ - `ApiProfileConfigMap`: per-profile API config shape
239
+ - `ApiProfileDefaultsMap`: defaults block within a profile
240
+ - `EndpointMap`: endpoint config shape
241
+
242
+ Example:
243
+
244
+ ```python
245
+ from etlplus.api import ApiConfigMap
246
+
247
+ api_cfg: ApiConfigMap = {
248
+ "base_url": "https://example.test",
249
+ "headers": {"Authorization": "Bearer token"},
250
+ "endpoints": {
251
+ "users": {
252
+ "path": "/users",
253
+ "method": "GET",
254
+ },
255
+ },
256
+ }
257
+ ```
258
+
228
259
  ## Supporting Modules
229
260
 
230
261
  - `etlplus.api.types` collects friendly aliases such as `Headers`, `Params`, `Url`, and
@@ -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',
@@ -3,11 +3,6 @@
3
3
 
4
4
  Configuration dataclasses for REST API services, profiles, and endpoints.
5
5
 
6
- These models used to live under :mod:`etlplus.config`, but they belong in the
7
- API layer because they compose runtime types such as
8
- :class:`etlplus.api.EndpointClient`, :class:`etlplus.api.PaginationConfig`, and
9
- :class:`etlplus.api.RateLimitConfig`.
10
-
11
6
  Notes
12
7
  -----
13
8
  - TypedDict references remain editor hints only; :meth:`from_obj` accepts
@@ -18,6 +13,7 @@ Notes
18
13
 
19
14
  from __future__ import annotations
20
15
 
16
+ from collections.abc import Callable
21
17
  from collections.abc import Mapping
22
18
  from dataclasses import dataclass
23
19
  from dataclasses import field
@@ -29,20 +25,20 @@ from typing import overload
29
25
  from urllib.parse import urlsplit
30
26
  from urllib.parse import urlunsplit
31
27
 
32
- from ..enums import HttpMethod
33
28
  from ..types import StrAnyMap
34
29
  from ..types import StrStrMap
35
30
  from ..utils import cast_str_dict
36
31
  from ..utils import coerce_dict
37
32
  from ..utils import maybe_mapping
38
33
  from .endpoint_client import EndpointClient
34
+ from .enums import HttpMethod
39
35
  from .pagination import PaginationConfig
40
36
  from .rate_limiting import RateLimitConfig
41
37
 
42
38
  if TYPE_CHECKING:
43
- from ..config.types import ApiConfigMap
44
- from ..config.types import ApiProfileConfigMap
45
- from ..config.types import EndpointMap
39
+ from .types import ApiConfigMap
40
+ from .types import ApiProfileConfigMap
41
+ from .types import EndpointMap
46
42
 
47
43
 
48
44
  # SECTION: EXPORTS ========================================================== #
@@ -106,6 +102,33 @@ def _effective_service_defaults(
106
102
  return fallback_base, fallback_headers
107
103
 
108
104
 
105
+ def _freeze_mapping(
106
+ mapping: Mapping[Any, Any],
107
+ *,
108
+ key_cast: Callable[[Any], Any] | None = None,
109
+ ) -> MappingProxyType:
110
+ """
111
+ Return an immutable copy of a mapping, optionally normalizing keys.
112
+
113
+ Parameters
114
+ ----------
115
+ mapping : Mapping[Any, Any]
116
+ Source mapping to freeze.
117
+ key_cast : Callable[[Any], Any] | None, optional
118
+ Optional key coercion applied to each key.
119
+
120
+ Returns
121
+ -------
122
+ MappingProxyType
123
+ Read-only mapping proxy with normalized keys.
124
+ """
125
+ if key_cast is None:
126
+ data = dict(mapping)
127
+ else:
128
+ data = {key_cast(key): value for key, value in mapping.items()}
129
+ return MappingProxyType(data)
130
+
131
+
109
132
  def _normalize_method(
110
133
  value: Any,
111
134
  ) -> Any | None:
@@ -232,16 +255,8 @@ class ApiProfileConfig:
232
255
  # -- Magic Methods (Object Lifecycle) -- #
233
256
 
234
257
  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
- )
258
+ object.__setattr__(self, 'headers', _freeze_mapping(self.headers))
259
+ object.__setattr__(self, 'auth', _freeze_mapping(self.auth))
245
260
 
246
261
  # -- Class Methods -- #
247
262
 
@@ -340,20 +355,16 @@ class ApiConfig:
340
355
  # -- Magic Methods (Object Lifecycle) -- #
341
356
 
342
357
  def __post_init__(self) -> None:
343
- object.__setattr__(
344
- self,
345
- 'headers',
346
- MappingProxyType(dict(self.headers)),
347
- )
358
+ object.__setattr__(self, 'headers', _freeze_mapping(self.headers))
348
359
  object.__setattr__(
349
360
  self,
350
361
  'endpoints',
351
- MappingProxyType({str(k): v for k, v in self.endpoints.items()}),
362
+ _freeze_mapping(self.endpoints, key_cast=str),
352
363
  )
353
364
  object.__setattr__(
354
365
  self,
355
366
  'profiles',
356
- MappingProxyType({str(k): v for k, v in self.profiles.items()}),
367
+ _freeze_mapping(self.profiles, key_cast=str),
357
368
  )
358
369
 
359
370
  # -- Internal Instance Methods -- #
@@ -545,12 +556,12 @@ class EndpointConfig:
545
556
  object.__setattr__(
546
557
  self,
547
558
  'path_params',
548
- MappingProxyType(dict(self.path_params)),
559
+ _freeze_mapping(self.path_params),
549
560
  )
550
561
  object.__setattr__(
551
562
  self,
552
563
  'query_params',
553
- MappingProxyType(dict(self.query_params)),
564
+ _freeze_mapping(self.query_params),
554
565
  )
555
566
 
556
567
  # -- 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)
@@ -20,8 +20,11 @@ Examples
20
20
  from __future__ import annotations
21
21
 
22
22
  from collections.abc import Callable
23
+ from collections.abc import Mapping
23
24
  from dataclasses import dataclass
24
25
  from typing import Any
26
+ from typing import Self
27
+ from typing import TypedDict
25
28
  from typing import cast
26
29
 
27
30
  from ..types import JSONData
@@ -39,6 +42,11 @@ __all__ = [
39
42
  'Headers',
40
43
  'Params',
41
44
  'Url',
45
+ # Typed Dicts
46
+ 'ApiConfigMap',
47
+ 'ApiProfileConfigMap',
48
+ 'ApiProfileDefaultsMap',
49
+ 'EndpointMap',
42
50
  ]
43
51
 
44
52
 
@@ -48,6 +56,88 @@ __all__ = [
48
56
  _UNSET = object()
49
57
 
50
58
 
59
+ # SECTION: TYPED DICTS ====================================================== #
60
+
61
+
62
+ class ApiConfigMap(TypedDict, total=False):
63
+ """
64
+ Top-level API config shape parsed by ApiConfig.from_obj.
65
+
66
+ Either provide a ``base_url`` with optional ``headers`` and ``endpoints``,
67
+ or provide ``profiles`` with at least one profile having a ``base_url``.
68
+
69
+ See Also
70
+ --------
71
+ - :class:`etlplus.api.config.ApiConfig`
72
+ """
73
+
74
+ base_url: str
75
+ headers: StrAnyMap
76
+ endpoints: Mapping[str, EndpointMap | str]
77
+ profiles: Mapping[str, ApiProfileConfigMap]
78
+
79
+
80
+ class ApiProfileConfigMap(TypedDict, total=False):
81
+ """
82
+ Shape accepted for a profile entry under ApiConfigMap.profiles.
83
+
84
+ Notes
85
+ -----
86
+ ``base_url`` is required at runtime when profiles are provided.
87
+
88
+ See Also
89
+ --------
90
+ - :class:`etlplus.api.config.ApiProfileConfig`
91
+ """
92
+
93
+ base_url: str
94
+ headers: StrAnyMap
95
+ base_path: str
96
+ auth: StrAnyMap
97
+ defaults: ApiProfileDefaultsMap
98
+
99
+
100
+ class ApiProfileDefaultsMap(TypedDict, total=False):
101
+ """
102
+ Defaults block available under a profile (all keys optional).
103
+
104
+ Notes
105
+ -----
106
+ Runtime expects header values to be str; typing remains permissive.
107
+
108
+ See Also
109
+ --------
110
+ - :class:`etlplus.api.config.ApiProfileConfig`
111
+ - :class:`etlplus.api.pagination.PaginationConfig`
112
+ - :class:`etlplus.api.rate_limiting.RateLimitConfig`
113
+ """
114
+
115
+ headers: StrAnyMap
116
+ pagination: Any
117
+ rate_limit: Any
118
+
119
+
120
+ class EndpointMap(TypedDict, total=False):
121
+ """
122
+ Shape accepted by EndpointConfig.from_obj.
123
+
124
+ One of ``path`` or ``url`` should be provided.
125
+
126
+ See Also
127
+ --------
128
+ - :class:`etlplus.api.config.EndpointConfig`
129
+ """
130
+
131
+ path: str
132
+ url: str
133
+ method: str
134
+ path_params: StrAnyMap
135
+ query_params: StrAnyMap
136
+ body: Any
137
+ pagination: Any
138
+ rate_limit: Any
139
+
140
+
51
141
  # SECTION: DATA CLASSES ===================================================== #
52
142
 
53
143
 
@@ -75,9 +165,9 @@ class RequestOptions:
75
165
  # -- Magic Methods (Object Lifecycle) -- #
76
166
 
77
167
  def __post_init__(self) -> None:
78
- if self.params:
168
+ if self.params is not None:
79
169
  object.__setattr__(self, 'params', dict(self.params))
80
- if self.headers:
170
+ if self.headers is not None:
81
171
  object.__setattr__(self, 'headers', dict(self.headers))
82
172
 
83
173
  # -- Instance Methods -- #
@@ -92,9 +182,9 @@ class RequestOptions:
92
182
  Keyword arguments for ``requests`` methods.
93
183
  """
94
184
  kw: dict[str, Any] = {}
95
- if self.params:
185
+ if self.params is not None:
96
186
  kw['params'] = dict(self.params)
97
- if self.headers:
187
+ if self.headers is not None:
98
188
  kw['headers'] = dict(self.headers)
99
189
  if self.timeout is not None:
100
190
  kw['timeout'] = self.timeout
@@ -106,7 +196,7 @@ class RequestOptions:
106
196
  params: Params | None | object = _UNSET,
107
197
  headers: Headers | None | object = _UNSET,
108
198
  timeout: float | None | object = _UNSET,
109
- ) -> RequestOptions:
199
+ ) -> Self:
110
200
  """
111
201
  Return a copy with the provided fields replaced.
112
202
 
@@ -146,7 +236,7 @@ class RequestOptions:
146
236
  else:
147
237
  next_timeout = cast(float | None, timeout)
148
238
 
149
- return RequestOptions(
239
+ return self.__class__(
150
240
  params=next_params,
151
241
  headers=next_headers,
152
242
  timeout=next_timeout,