etlplus 0.11.2__tar.gz → 0.11.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 (159) hide show
  1. {etlplus-0.11.2/etlplus.egg-info → etlplus-0.11.4}/PKG-INFO +1 -1
  2. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/file/__init__.py +0 -2
  3. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/file/enums.py +0 -28
  4. {etlplus-0.11.2 → etlplus-0.11.4/etlplus.egg-info}/PKG-INFO +1 -1
  5. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus.egg-info/SOURCES.txt +3 -1
  6. etlplus-0.11.2/tests/unit/test_u_file.py → etlplus-0.11.4/tests/unit/file/test_u_file_core.py +2 -150
  7. etlplus-0.11.4/tests/unit/file/test_u_file_enums.py +90 -0
  8. etlplus-0.11.4/tests/unit/file/test_u_file_yaml.py +110 -0
  9. {etlplus-0.11.2 → etlplus-0.11.4}/.coveragerc +0 -0
  10. {etlplus-0.11.2 → etlplus-0.11.4}/.editorconfig +0 -0
  11. {etlplus-0.11.2 → etlplus-0.11.4}/.gitattributes +0 -0
  12. {etlplus-0.11.2 → etlplus-0.11.4}/.github/actions/python-bootstrap/action.yml +0 -0
  13. {etlplus-0.11.2 → etlplus-0.11.4}/.github/workflows/ci.yml +0 -0
  14. {etlplus-0.11.2 → etlplus-0.11.4}/.gitignore +0 -0
  15. {etlplus-0.11.2 → etlplus-0.11.4}/.pre-commit-config.yaml +0 -0
  16. {etlplus-0.11.2 → etlplus-0.11.4}/.ruff.toml +0 -0
  17. {etlplus-0.11.2 → etlplus-0.11.4}/CODE_OF_CONDUCT.md +0 -0
  18. {etlplus-0.11.2 → etlplus-0.11.4}/CONTRIBUTING.md +0 -0
  19. {etlplus-0.11.2 → etlplus-0.11.4}/DEMO.md +0 -0
  20. {etlplus-0.11.2 → etlplus-0.11.4}/LICENSE +0 -0
  21. {etlplus-0.11.2 → etlplus-0.11.4}/MANIFEST.in +0 -0
  22. {etlplus-0.11.2 → etlplus-0.11.4}/Makefile +0 -0
  23. {etlplus-0.11.2 → etlplus-0.11.4}/README.md +0 -0
  24. {etlplus-0.11.2 → etlplus-0.11.4}/REFERENCES.md +0 -0
  25. {etlplus-0.11.2 → etlplus-0.11.4}/docs/README.md +0 -0
  26. {etlplus-0.11.2 → etlplus-0.11.4}/docs/pipeline-guide.md +0 -0
  27. {etlplus-0.11.2 → etlplus-0.11.4}/docs/snippets/installation_version.md +0 -0
  28. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/__init__.py +0 -0
  29. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/__main__.py +0 -0
  30. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/__version__.py +0 -0
  31. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/README.md +0 -0
  32. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/__init__.py +0 -0
  33. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/auth.py +0 -0
  34. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/config.py +0 -0
  35. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/endpoint_client.py +0 -0
  36. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/errors.py +0 -0
  37. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/pagination/__init__.py +0 -0
  38. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/pagination/client.py +0 -0
  39. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/pagination/config.py +0 -0
  40. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/pagination/paginator.py +0 -0
  41. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/rate_limiting/__init__.py +0 -0
  42. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/rate_limiting/config.py +0 -0
  43. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/rate_limiting/rate_limiter.py +0 -0
  44. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/request_manager.py +0 -0
  45. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/retry_manager.py +0 -0
  46. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/transport.py +0 -0
  47. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/api/types.py +0 -0
  48. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/__init__.py +0 -0
  49. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/commands.py +0 -0
  50. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/constants.py +0 -0
  51. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/handlers.py +0 -0
  52. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/io.py +0 -0
  53. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/main.py +0 -0
  54. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/options.py +0 -0
  55. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/state.py +0 -0
  56. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/cli/types.py +0 -0
  57. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/config/__init__.py +0 -0
  58. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/config/connector.py +0 -0
  59. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/config/jobs.py +0 -0
  60. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/config/pipeline.py +0 -0
  61. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/config/profile.py +0 -0
  62. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/config/types.py +0 -0
  63. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/config/utils.py +0 -0
  64. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/database/__init__.py +0 -0
  65. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/database/ddl.py +0 -0
  66. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/database/engine.py +0 -0
  67. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/database/orm.py +0 -0
  68. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/database/schema.py +0 -0
  69. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/database/types.py +0 -0
  70. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/enums.py +0 -0
  71. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/extract.py +0 -0
  72. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/file/core.py +0 -0
  73. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/file/csv.py +0 -0
  74. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/file/json.py +0 -0
  75. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/file/xml.py +0 -0
  76. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/file/yaml.py +0 -0
  77. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/load.py +0 -0
  78. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/mixins.py +0 -0
  79. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/py.typed +0 -0
  80. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/run.py +0 -0
  81. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/run_helpers.py +0 -0
  82. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/templates/__init__.py +0 -0
  83. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/templates/ddl.sql.j2 +0 -0
  84. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/templates/view.sql.j2 +0 -0
  85. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/transform.py +0 -0
  86. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/types.py +0 -0
  87. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/utils.py +0 -0
  88. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/validate.py +0 -0
  89. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/validation/__init__.py +0 -0
  90. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus/validation/utils.py +0 -0
  91. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus.egg-info/dependency_links.txt +0 -0
  92. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus.egg-info/entry_points.txt +0 -0
  93. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus.egg-info/requires.txt +0 -0
  94. {etlplus-0.11.2 → etlplus-0.11.4}/etlplus.egg-info/top_level.txt +0 -0
  95. {etlplus-0.11.2 → etlplus-0.11.4}/examples/README.md +0 -0
  96. {etlplus-0.11.2 → etlplus-0.11.4}/examples/configs/ddl_spec.yml +0 -0
  97. {etlplus-0.11.2 → etlplus-0.11.4}/examples/configs/pipeline.yml +0 -0
  98. {etlplus-0.11.2 → etlplus-0.11.4}/examples/data/sample.csv +0 -0
  99. {etlplus-0.11.2 → etlplus-0.11.4}/examples/data/sample.json +0 -0
  100. {etlplus-0.11.2 → etlplus-0.11.4}/examples/data/sample.xml +0 -0
  101. {etlplus-0.11.2 → etlplus-0.11.4}/examples/data/sample.xsd +0 -0
  102. {etlplus-0.11.2 → etlplus-0.11.4}/examples/data/sample.yaml +0 -0
  103. {etlplus-0.11.2 → etlplus-0.11.4}/examples/quickstart_python.py +0 -0
  104. {etlplus-0.11.2 → etlplus-0.11.4}/pyproject.toml +0 -0
  105. {etlplus-0.11.2 → etlplus-0.11.4}/pytest.ini +0 -0
  106. {etlplus-0.11.2 → etlplus-0.11.4}/setup.cfg +0 -0
  107. {etlplus-0.11.2 → etlplus-0.11.4}/setup.py +0 -0
  108. {etlplus-0.11.2 → etlplus-0.11.4}/tests/__init__.py +0 -0
  109. {etlplus-0.11.2 → etlplus-0.11.4}/tests/conftest.py +0 -0
  110. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/conftest.py +0 -0
  111. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_cli.py +0 -0
  112. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_examples_data_parity.py +0 -0
  113. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_pagination_strategy.py +0 -0
  114. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_pipeline_smoke.py +0 -0
  115. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_pipeline_yaml_load.py +0 -0
  116. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_run.py +0 -0
  117. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_run_profile_pagination_defaults.py +0 -0
  118. {etlplus-0.11.2 → etlplus-0.11.4}/tests/integration/test_i_run_profile_rate_limit_defaults.py +0 -0
  119. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/conftest.py +0 -0
  120. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_auth.py +0 -0
  121. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_config.py +0 -0
  122. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_endpoint_client.py +0 -0
  123. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_mocks.py +0 -0
  124. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_pagination_client.py +0 -0
  125. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_pagination_config.py +0 -0
  126. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_paginator.py +0 -0
  127. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_rate_limit_config.py +0 -0
  128. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_rate_limiter.py +0 -0
  129. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_request_manager.py +0 -0
  130. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_retry_manager.py +0 -0
  131. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_transport.py +0 -0
  132. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/api/test_u_types.py +0 -0
  133. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/cli/conftest.py +0 -0
  134. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/cli/test_u_cli_handlers.py +0 -0
  135. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/cli/test_u_cli_io.py +0 -0
  136. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/cli/test_u_cli_main.py +0 -0
  137. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/cli/test_u_cli_state.py +0 -0
  138. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/config/test_u_config_utils.py +0 -0
  139. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/config/test_u_connector.py +0 -0
  140. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/config/test_u_jobs.py +0 -0
  141. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/config/test_u_pipeline.py +0 -0
  142. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/conftest.py +0 -0
  143. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/database/test_u_database_ddl.py +0 -0
  144. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/database/test_u_database_engine.py +0 -0
  145. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/database/test_u_database_orm.py +0 -0
  146. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/database/test_u_database_schema.py +0 -0
  147. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_enums.py +0 -0
  148. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_extract.py +0 -0
  149. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_load.py +0 -0
  150. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_main.py +0 -0
  151. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_mixins.py +0 -0
  152. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_run.py +0 -0
  153. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_run_helpers.py +0 -0
  154. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_transform.py +0 -0
  155. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_utils.py +0 -0
  156. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_validate.py +0 -0
  157. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/test_u_version.py +0 -0
  158. {etlplus-0.11.2 → etlplus-0.11.4}/tests/unit/validation/test_u_validation_utils.py +0 -0
  159. {etlplus-0.11.2 → etlplus-0.11.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.11.2
3
+ Version: 0.11.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
@@ -9,7 +9,6 @@ from __future__ import annotations
9
9
  from .core import File
10
10
  from .enums import CompressionFormat
11
11
  from .enums import FileFormat
12
- from .enums import coerce_file_format
13
12
  from .enums import infer_file_format_and_compression
14
13
 
15
14
  # SECTION: EXPORTS ========================================================== #
@@ -22,6 +21,5 @@ __all__ = [
22
21
  'CompressionFormat',
23
22
  'FileFormat',
24
23
  # Functions
25
- 'coerce_file_format',
26
24
  'infer_file_format_and_compression',
27
25
  ]
@@ -16,8 +16,6 @@ from ..types import StrStrMap
16
16
  __all__ = [
17
17
  'CompressionFormat',
18
18
  'FileFormat',
19
- 'coerce_compression_format',
20
- 'coerce_file_format',
21
19
  'infer_file_format_and_compression',
22
20
  ]
23
21
 
@@ -164,32 +162,6 @@ _COMPRESSION_FILE_FORMATS: set[FileFormat] = {
164
162
  # SECTION: FUNCTIONS ======================================================== #
165
163
 
166
164
 
167
- # TODO: Deprecate in favor of using the enum methods directly.
168
- def coerce_compression_format(
169
- compression_format: CompressionFormat | str,
170
- ) -> CompressionFormat:
171
- """
172
- Normalize textual compression format values to :class:`CompressionFormat`.
173
-
174
- This thin wrapper is kept for backward compatibility; prefer
175
- :meth:`CompressionFormat.coerce` going forward.
176
- """
177
- return CompressionFormat.coerce(compression_format)
178
-
179
-
180
- # TODO: Deprecate in favor of using the enum methods directly.
181
- def coerce_file_format(
182
- file_format: FileFormat | str,
183
- ) -> FileFormat:
184
- """
185
- Normalize textual file format values to :class:`FileFormat`.
186
-
187
- This thin wrapper is kept for backward compatibility; prefer
188
- :meth:`FileFormat.coerce` going forward.
189
- """
190
- return FileFormat.coerce(file_format)
191
-
192
-
193
165
  # TODO: Convert to a method on FileFormat or CompressionFormat?
194
166
  def infer_file_format_and_compression(
195
167
  value: object,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.11.2
3
+ Version: 0.11.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
@@ -114,7 +114,6 @@ tests/integration/test_i_run_profile_rate_limit_defaults.py
114
114
  tests/unit/conftest.py
115
115
  tests/unit/test_u_enums.py
116
116
  tests/unit/test_u_extract.py
117
- tests/unit/test_u_file.py
118
117
  tests/unit/test_u_load.py
119
118
  tests/unit/test_u_main.py
120
119
  tests/unit/test_u_mixins.py
@@ -151,5 +150,8 @@ tests/unit/database/test_u_database_ddl.py
151
150
  tests/unit/database/test_u_database_engine.py
152
151
  tests/unit/database/test_u_database_orm.py
153
152
  tests/unit/database/test_u_database_schema.py
153
+ tests/unit/file/test_u_file_core.py
154
+ tests/unit/file/test_u_file_enums.py
155
+ tests/unit/file/test_u_file_yaml.py
154
156
  tests/unit/validation/test_u_validation_utils.py
155
157
  tools/update_demo_snippets.py
@@ -1,7 +1,7 @@
1
1
  """
2
- :mod:`tests.unit.test_u_file` module.
2
+ :mod:`tests.unit.test_u_file_core` module.
3
3
 
4
- Unit tests for :mod:`etlplus.file`.
4
+ Unit tests for :mod:`etlplus.file.core`.
5
5
 
6
6
  Notes
7
7
  -----
@@ -11,17 +11,13 @@ Notes
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
- from collections.abc import Generator
15
14
  from pathlib import Path
16
15
  from typing import cast
17
16
 
18
17
  import pytest
19
18
 
20
- import etlplus.file.yaml as yaml_module
21
- from etlplus.file import CompressionFormat
22
19
  from etlplus.file import File
23
20
  from etlplus.file import FileFormat
24
- from etlplus.file import infer_file_format_and_compression
25
21
  from etlplus.types import JSONDict
26
22
 
27
23
  # SECTION: HELPERS ========================================================== #
@@ -30,46 +26,6 @@ from etlplus.types import JSONDict
30
26
  pytestmark = pytest.mark.unit
31
27
 
32
28
 
33
- class _StubYaml:
34
- """Minimal PyYAML substitute to avoid optional dependency in tests."""
35
-
36
- def __init__(self) -> None:
37
- self.dump_calls: list[dict[str, object]] = []
38
-
39
- def safe_load(
40
- self,
41
- handle: object,
42
- ) -> dict[str, str]:
43
- """Stub for PyYAML's ``safe_load`` function."""
44
- text = ''
45
- if hasattr(handle, 'read'): # type: ignore[call-arg]
46
- text = handle.read()
47
- return {'loaded': str(text).strip()}
48
-
49
- def safe_dump(
50
- self,
51
- data: object,
52
- handle: object,
53
- **kwargs: object,
54
- ) -> None:
55
- """Stub for PyYAML's ``safe_dump`` function."""
56
- self.dump_calls.append({'data': data, 'kwargs': kwargs})
57
- if hasattr(handle, 'write'):
58
- handle.write('yaml') # type: ignore[call-arg]
59
-
60
-
61
- @pytest.fixture(name='yaml_stub')
62
- def yaml_stub_fixture() -> Generator[_StubYaml]:
63
- """Install a stub PyYAML module for YAML tests."""
64
- # pylint: disable=protected-access
65
-
66
- stub = _StubYaml()
67
- yaml_module._YAML_CACHE.clear()
68
- yaml_module._YAML_CACHE['mod'] = stub
69
- yield stub
70
- yaml_module._YAML_CACHE.clear()
71
-
72
-
73
29
  # SECTION: TESTS ============================================================ #
74
30
 
75
31
 
@@ -303,107 +259,3 @@ class TestFile:
303
259
  text = path.read_text(encoding='utf-8')
304
260
  assert text.startswith('<?xml')
305
261
  assert '<records>' in text
306
-
307
-
308
- class TestFileFormat:
309
- """Unit test suite for :class:`etlplus.enums.FileFormat`."""
310
-
311
- @pytest.mark.parametrize(
312
- 'value,expected',
313
- [
314
- ('JSON', FileFormat.JSON),
315
- ('application/xml', FileFormat.XML),
316
- ('yml', FileFormat.YAML),
317
- ],
318
- )
319
- def test_aliases(
320
- self,
321
- value: str,
322
- expected: FileFormat,
323
- ) -> None:
324
- """Test alias coercions."""
325
- assert FileFormat.coerce(value) is expected
326
-
327
- def test_coerce(self) -> None:
328
- """Test :meth:`coerce`."""
329
- assert FileFormat.coerce('csv') is FileFormat.CSV
330
-
331
- def test_invalid_value(self) -> None:
332
- """Test that invalid values raise ValueError."""
333
- with pytest.raises(ValueError, match='Invalid FileFormat'):
334
- FileFormat.coerce('ini')
335
-
336
-
337
- class TestInferFileFormatAndCompression:
338
- """Unit test suite for :func:`infer_file_format_and_compression`."""
339
-
340
- @pytest.mark.parametrize(
341
- 'value,filename,expected_format,expected_compression',
342
- [
343
- ('data.csv.gz', None, FileFormat.CSV, CompressionFormat.GZ),
344
- ('data.jsonl.gz', None, FileFormat.NDJSON, CompressionFormat.GZ),
345
- ('data.zip', None, None, CompressionFormat.ZIP),
346
- ('application/json; charset=utf-8', None, FileFormat.JSON, None),
347
- ('application/gzip', None, None, CompressionFormat.GZ),
348
- (
349
- 'application/octet-stream',
350
- 'payload.csv.gz',
351
- FileFormat.CSV,
352
- CompressionFormat.GZ,
353
- ),
354
- ('application/octet-stream', None, None, None),
355
- (FileFormat.GZ, None, None, CompressionFormat.GZ),
356
- (CompressionFormat.ZIP, None, None, CompressionFormat.ZIP),
357
- ],
358
- )
359
- def test_infers_format_and_compression(
360
- self,
361
- value: object,
362
- filename: object | None,
363
- expected_format: FileFormat | None,
364
- expected_compression: CompressionFormat | None,
365
- ) -> None:
366
- """Test mixed inputs for format and compression inference."""
367
- fmt, compression = infer_file_format_and_compression(value, filename)
368
- assert fmt is expected_format
369
- assert compression is expected_compression
370
-
371
-
372
- @pytest.mark.unit
373
- class TestYamlSupport:
374
- """Unit tests exercising YAML read/write helpers without PyYAML."""
375
-
376
- def test_read_yaml_uses_stub(
377
- self,
378
- tmp_path: Path,
379
- yaml_stub: _StubYaml,
380
- ) -> None:
381
- """
382
- Test reading YAML should invoke stub ``safe_load``.
383
- """
384
- # pylint: disable=protected-access
385
-
386
- assert yaml_module._YAML_CACHE['mod'] is yaml_stub
387
- path = tmp_path / 'data.yaml'
388
- path.write_text('name: etl', encoding='utf-8')
389
-
390
- result = File(path, FileFormat.YAML).read()
391
-
392
- assert result == {'loaded': 'name: etl'}
393
-
394
- def test_write_yaml_uses_stub(
395
- self,
396
- tmp_path: Path,
397
- yaml_stub: _StubYaml,
398
- ) -> None:
399
- """
400
- Test writing YAML should invoke stub ``safe_dump``.
401
- """
402
- path = tmp_path / 'data.yaml'
403
- payload = [{'name': 'etl'}]
404
-
405
- written = File(path, FileFormat.YAML).write(payload)
406
-
407
- assert written == 1
408
- assert yaml_stub.dump_calls
409
- assert yaml_stub.dump_calls[0]['data'] == payload
@@ -0,0 +1,90 @@
1
+ """
2
+ :mod:`tests.unit.test_u_file_enums` module.
3
+
4
+ Unit tests for :mod:`etlplus.file.enums`.
5
+
6
+ Notes
7
+ -----
8
+ - Uses ``tmp_path`` for filesystem isolation.
9
+ - Exercises JSON detection and defers errors for unknown extensions.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import pytest
15
+
16
+ from etlplus.file import CompressionFormat
17
+ from etlplus.file import FileFormat
18
+ from etlplus.file import infer_file_format_and_compression
19
+
20
+ # SECTION: HELPERS ========================================================== #
21
+
22
+
23
+ pytestmark = pytest.mark.unit
24
+
25
+
26
+ # SECTION: TESTS ============================================================ #
27
+
28
+
29
+ class TestFileFormat:
30
+ """Unit test suite for :class:`etlplus.enums.FileFormat`."""
31
+
32
+ @pytest.mark.parametrize(
33
+ 'value,expected',
34
+ [
35
+ ('JSON', FileFormat.JSON),
36
+ ('application/xml', FileFormat.XML),
37
+ ('yml', FileFormat.YAML),
38
+ ],
39
+ )
40
+ def test_aliases(
41
+ self,
42
+ value: str,
43
+ expected: FileFormat,
44
+ ) -> None:
45
+ """Test alias coercions."""
46
+ assert FileFormat.coerce(value) is expected
47
+
48
+ def test_coerce(self) -> None:
49
+ """Test :meth:`coerce`."""
50
+ assert FileFormat.coerce('csv') is FileFormat.CSV
51
+
52
+ def test_invalid_value(self) -> None:
53
+ """Test that invalid values raise ValueError."""
54
+ with pytest.raises(ValueError, match='Invalid FileFormat'):
55
+ FileFormat.coerce('ini')
56
+
57
+
58
+ class TestInferFileFormatAndCompression:
59
+ """Unit test suite for :func:`infer_file_format_and_compression`."""
60
+
61
+ @pytest.mark.parametrize(
62
+ 'value,filename,expected_format,expected_compression',
63
+ [
64
+ ('data.csv.gz', None, FileFormat.CSV, CompressionFormat.GZ),
65
+ ('data.jsonl.gz', None, FileFormat.NDJSON, CompressionFormat.GZ),
66
+ ('data.zip', None, None, CompressionFormat.ZIP),
67
+ ('application/json; charset=utf-8', None, FileFormat.JSON, None),
68
+ ('application/gzip', None, None, CompressionFormat.GZ),
69
+ (
70
+ 'application/octet-stream',
71
+ 'payload.csv.gz',
72
+ FileFormat.CSV,
73
+ CompressionFormat.GZ,
74
+ ),
75
+ ('application/octet-stream', None, None, None),
76
+ (FileFormat.GZ, None, None, CompressionFormat.GZ),
77
+ (CompressionFormat.ZIP, None, None, CompressionFormat.ZIP),
78
+ ],
79
+ )
80
+ def test_infers_format_and_compression(
81
+ self,
82
+ value: object,
83
+ filename: object | None,
84
+ expected_format: FileFormat | None,
85
+ expected_compression: CompressionFormat | None,
86
+ ) -> None:
87
+ """Test mixed inputs for format and compression inference."""
88
+ fmt, compression = infer_file_format_and_compression(value, filename)
89
+ assert fmt is expected_format
90
+ assert compression is expected_compression
@@ -0,0 +1,110 @@
1
+ """
2
+ :mod:`tests.unit.test_u_file_yaml` module.
3
+
4
+ Unit tests for :mod:`etlplus.file.yaml`.
5
+
6
+ Notes
7
+ -----
8
+ - Uses ``tmp_path`` for filesystem isolation.
9
+ - Exercises JSON detection and defers errors for unknown extensions.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from collections.abc import Generator
15
+ from pathlib import Path
16
+
17
+ import pytest
18
+
19
+ import etlplus.file.yaml as yaml_module
20
+ from etlplus.file import File
21
+ from etlplus.file import FileFormat
22
+
23
+ # SECTION: HELPERS ========================================================== #
24
+
25
+
26
+ pytestmark = pytest.mark.unit
27
+
28
+
29
+ class _StubYaml:
30
+ """Minimal PyYAML substitute to avoid optional dependency in tests."""
31
+
32
+ def __init__(self) -> None:
33
+ self.dump_calls: list[dict[str, object]] = []
34
+
35
+ def safe_load(
36
+ self,
37
+ handle: object,
38
+ ) -> dict[str, str]:
39
+ """Stub for PyYAML's ``safe_load`` function."""
40
+ text = ''
41
+ if hasattr(handle, 'read'): # type: ignore[call-arg]
42
+ text = handle.read()
43
+ return {'loaded': str(text).strip()}
44
+
45
+ def safe_dump(
46
+ self,
47
+ data: object,
48
+ handle: object,
49
+ **kwargs: object,
50
+ ) -> None:
51
+ """Stub for PyYAML's ``safe_dump`` function."""
52
+ self.dump_calls.append({'data': data, 'kwargs': kwargs})
53
+ if hasattr(handle, 'write'):
54
+ handle.write('yaml') # type: ignore[call-arg]
55
+
56
+
57
+ @pytest.fixture(name='yaml_stub')
58
+ def yaml_stub_fixture() -> Generator[_StubYaml]:
59
+ """Install a stub PyYAML module for YAML tests."""
60
+ # pylint: disable=protected-access
61
+
62
+ stub = _StubYaml()
63
+ yaml_module._YAML_CACHE.clear()
64
+ yaml_module._YAML_CACHE['mod'] = stub
65
+ yield stub
66
+ yaml_module._YAML_CACHE.clear()
67
+
68
+
69
+ # SECTION: TESTS ============================================================ #
70
+
71
+
72
+ # TODO: Can File read/write YAML without PyYAML installed?
73
+ # TODO: If not, remove this unit test suite.
74
+ class TestYamlSupport:
75
+ """Unit tests exercising YAML read/write helpers without PyYAML."""
76
+
77
+ def test_read_yaml_uses_stub(
78
+ self,
79
+ tmp_path: Path,
80
+ yaml_stub: _StubYaml,
81
+ ) -> None:
82
+ """
83
+ Test reading YAML should invoke stub ``safe_load``.
84
+ """
85
+ # pylint: disable=protected-access
86
+
87
+ assert yaml_module._YAML_CACHE['mod'] is yaml_stub
88
+ path = tmp_path / 'data.yaml'
89
+ path.write_text('name: etl', encoding='utf-8')
90
+
91
+ result = File(path, FileFormat.YAML).read()
92
+
93
+ assert result == {'loaded': 'name: etl'}
94
+
95
+ def test_write_yaml_uses_stub(
96
+ self,
97
+ tmp_path: Path,
98
+ yaml_stub: _StubYaml,
99
+ ) -> None:
100
+ """
101
+ Test writing YAML should invoke stub ``safe_dump``.
102
+ """
103
+ path = tmp_path / 'data.yaml'
104
+ payload = [{'name': 'etl'}]
105
+
106
+ written = File(path, FileFormat.YAML).write(payload)
107
+
108
+ assert written == 1
109
+ assert yaml_stub.dump_calls
110
+ assert yaml_stub.dump_calls[0]['data'] == payload
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes