etlplus 0.14.3__py3-none-any.whl → 0.16.0__py3-none-any.whl

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 (122) hide show
  1. etlplus/README.md +4 -4
  2. etlplus/api/README.md +33 -2
  3. etlplus/api/auth.py +1 -1
  4. etlplus/api/config.py +5 -10
  5. etlplus/api/endpoint_client.py +4 -4
  6. etlplus/api/pagination/config.py +1 -1
  7. etlplus/api/pagination/paginator.py +6 -7
  8. etlplus/api/rate_limiting/config.py +4 -4
  9. etlplus/api/rate_limiting/rate_limiter.py +1 -1
  10. etlplus/api/retry_manager.py +2 -2
  11. etlplus/api/transport.py +1 -1
  12. etlplus/api/types.py +99 -0
  13. etlplus/api/utils.py +6 -2
  14. etlplus/cli/README.md +2 -2
  15. etlplus/cli/commands.py +75 -42
  16. etlplus/cli/constants.py +1 -1
  17. etlplus/cli/handlers.py +33 -15
  18. etlplus/cli/io.py +2 -2
  19. etlplus/cli/main.py +2 -2
  20. etlplus/cli/state.py +4 -7
  21. etlplus/connector/__init__.py +43 -0
  22. etlplus/connector/api.py +161 -0
  23. etlplus/connector/connector.py +26 -0
  24. etlplus/connector/core.py +132 -0
  25. etlplus/connector/database.py +122 -0
  26. etlplus/connector/enums.py +52 -0
  27. etlplus/connector/file.py +120 -0
  28. etlplus/connector/types.py +40 -0
  29. etlplus/connector/utils.py +122 -0
  30. etlplus/database/README.md +2 -2
  31. etlplus/database/ddl.py +2 -2
  32. etlplus/database/engine.py +19 -3
  33. etlplus/database/orm.py +2 -0
  34. etlplus/enums.py +1 -33
  35. etlplus/file/README.md +2 -2
  36. etlplus/file/_imports.py +1 -0
  37. etlplus/file/_io.py +52 -4
  38. etlplus/file/accdb.py +3 -2
  39. etlplus/file/arrow.py +3 -2
  40. etlplus/file/avro.py +3 -2
  41. etlplus/file/bson.py +3 -2
  42. etlplus/file/cbor.py +3 -2
  43. etlplus/file/cfg.py +3 -2
  44. etlplus/file/conf.py +3 -2
  45. etlplus/file/core.py +11 -8
  46. etlplus/file/csv.py +3 -2
  47. etlplus/file/dat.py +3 -2
  48. etlplus/file/dta.py +3 -2
  49. etlplus/file/duckdb.py +3 -2
  50. etlplus/file/enums.py +1 -1
  51. etlplus/file/feather.py +3 -2
  52. etlplus/file/fwf.py +3 -2
  53. etlplus/file/gz.py +3 -2
  54. etlplus/file/hbs.py +3 -2
  55. etlplus/file/hdf5.py +3 -2
  56. etlplus/file/ini.py +3 -2
  57. etlplus/file/ion.py +3 -2
  58. etlplus/file/jinja2.py +3 -2
  59. etlplus/file/json.py +5 -16
  60. etlplus/file/log.py +3 -2
  61. etlplus/file/mat.py +3 -2
  62. etlplus/file/mdb.py +3 -2
  63. etlplus/file/msgpack.py +3 -2
  64. etlplus/file/mustache.py +3 -2
  65. etlplus/file/nc.py +3 -2
  66. etlplus/file/ndjson.py +3 -2
  67. etlplus/file/numbers.py +3 -2
  68. etlplus/file/ods.py +3 -2
  69. etlplus/file/orc.py +3 -2
  70. etlplus/file/parquet.py +3 -2
  71. etlplus/file/pb.py +3 -2
  72. etlplus/file/pbf.py +3 -2
  73. etlplus/file/properties.py +3 -2
  74. etlplus/file/proto.py +3 -2
  75. etlplus/file/psv.py +3 -2
  76. etlplus/file/rda.py +3 -2
  77. etlplus/file/rds.py +3 -2
  78. etlplus/file/sas7bdat.py +3 -2
  79. etlplus/file/sav.py +3 -2
  80. etlplus/file/sqlite.py +3 -2
  81. etlplus/file/stub.py +1 -0
  82. etlplus/file/sylk.py +3 -2
  83. etlplus/file/tab.py +3 -2
  84. etlplus/file/toml.py +3 -2
  85. etlplus/file/tsv.py +3 -2
  86. etlplus/file/txt.py +4 -3
  87. etlplus/file/vm.py +3 -2
  88. etlplus/file/wks.py +3 -2
  89. etlplus/file/xls.py +3 -2
  90. etlplus/file/xlsm.py +3 -2
  91. etlplus/file/xlsx.py +3 -2
  92. etlplus/file/xml.py +9 -3
  93. etlplus/file/xpt.py +3 -2
  94. etlplus/file/yaml.py +5 -16
  95. etlplus/file/zip.py +3 -2
  96. etlplus/file/zsav.py +3 -2
  97. etlplus/ops/extract.py +13 -1
  98. etlplus/ops/load.py +15 -2
  99. etlplus/ops/run.py +18 -13
  100. etlplus/ops/transform.py +2 -2
  101. etlplus/ops/utils.py +6 -35
  102. etlplus/ops/validate.py +3 -3
  103. etlplus/templates/README.md +2 -2
  104. etlplus/types.py +3 -2
  105. etlplus/utils.py +163 -29
  106. etlplus/{config → workflow}/README.md +6 -6
  107. etlplus/workflow/__init__.py +32 -0
  108. etlplus/{dag.py → workflow/dag.py} +6 -4
  109. etlplus/{config → workflow}/jobs.py +101 -38
  110. etlplus/{config → workflow}/pipeline.py +59 -51
  111. etlplus/{config → workflow}/profile.py +8 -5
  112. {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/METADATA +4 -4
  113. etlplus-0.16.0.dist-info/RECORD +141 -0
  114. {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/WHEEL +1 -1
  115. etlplus/config/__init__.py +0 -56
  116. etlplus/config/connector.py +0 -372
  117. etlplus/config/types.py +0 -204
  118. etlplus/config/utils.py +0 -120
  119. etlplus-0.14.3.dist-info/RECORD +0 -135
  120. {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/entry_points.txt +0 -0
  121. {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/licenses/LICENSE +0 -0
  122. {etlplus-0.14.3.dist-info → etlplus-0.16.0.dist-info}/top_level.txt +0 -0
etlplus/utils.py CHANGED
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import json
10
10
  from collections.abc import Callable
11
+ from collections.abc import Iterable
11
12
  from collections.abc import Mapping
12
13
  from typing import Any
13
14
  from typing import TypeVar
@@ -25,6 +26,7 @@ __all__ = [
25
26
  # Mapping utilities
26
27
  'cast_str_dict',
27
28
  'coerce_dict',
29
+ 'deep_substitute',
28
30
  'maybe_mapping',
29
31
  # Float coercion
30
32
  'to_float',
@@ -39,7 +41,8 @@ __all__ = [
39
41
  # Generic number coercion
40
42
  'to_number',
41
43
  # Text processing
42
- 'normalized_str',
44
+ 'normalize_choice',
45
+ 'normalize_str',
43
46
  ]
44
47
 
45
48
 
@@ -56,6 +59,52 @@ Num = TypeVar('Num', int, float)
56
59
  # -- Data Utilities -- #
57
60
 
58
61
 
62
+ def deep_substitute(
63
+ value: Any,
64
+ vars_map: StrAnyMap | None,
65
+ env_map: Mapping[str, str] | None,
66
+ ) -> Any:
67
+ """
68
+ Recursively substitute ``${VAR}`` tokens in nested structures.
69
+
70
+ Only strings are substituted; other types are returned as-is.
71
+
72
+ Parameters
73
+ ----------
74
+ value : Any
75
+ The value to perform substitutions on.
76
+ vars_map : StrAnyMap | None
77
+ Mapping of variable names to replacement values (lower precedence).
78
+ env_map : Mapping[str, str] | None
79
+ Mapping of environment variables overriding *vars_map* values (higher
80
+ precedence).
81
+
82
+ Returns
83
+ -------
84
+ Any
85
+ New structure with substitutions applied where tokens were found.
86
+ """
87
+ substitutions = _prepare_substitutions(vars_map, env_map)
88
+
89
+ def _apply(node: Any) -> Any:
90
+ match node:
91
+ case str():
92
+ return _replace_tokens(node, substitutions)
93
+ case Mapping():
94
+ return {k: _apply(v) for k, v in node.items()}
95
+ case list() | tuple() as seq:
96
+ apply = [_apply(item) for item in seq]
97
+ return apply if isinstance(seq, list) else tuple(apply)
98
+ case set():
99
+ return {_apply(item) for item in node}
100
+ case frozenset():
101
+ return frozenset(_apply(item) for item in node)
102
+ case _:
103
+ return node
104
+
105
+ return _apply(value)
106
+
107
+
59
108
  def cast_str_dict(
60
109
  mapping: StrAnyMap | None,
61
110
  ) -> dict[str, str]:
@@ -81,7 +130,7 @@ def coerce_dict(
81
130
  value: Any,
82
131
  ) -> dict[str, Any]:
83
132
  """
84
- Return a ``dict`` copy when ``value`` is mapping-like.
133
+ Return a ``dict`` copy when *value* is mapping-like.
85
134
 
86
135
  Parameters
87
136
  ----------
@@ -91,7 +140,7 @@ def coerce_dict(
91
140
  Returns
92
141
  -------
93
142
  dict[str, Any]
94
- Shallow copy of ``value`` converted to a standard ``dict``.
143
+ Shallow copy of *value* converted to a standard ``dict``.
95
144
  """
96
145
  return dict(value) if isinstance(value, Mapping) else {}
97
146
 
@@ -121,7 +170,7 @@ def maybe_mapping(
121
170
  value: Any,
122
171
  ) -> StrAnyMap | None:
123
172
  """
124
- Return ``value`` when it is mapping-like; otherwise ``None``.
173
+ Return *value* when it is mapping-like; otherwise ``None``.
125
174
 
126
175
  Parameters
127
176
  ----------
@@ -140,7 +189,7 @@ def print_json(
140
189
  obj: Any,
141
190
  ) -> None:
142
191
  """
143
- Pretty-print ``obj`` as UTF-8 JSON without ASCII escaping.
192
+ Pretty-print *obj* as UTF-8 JSON without ASCII escaping.
144
193
 
145
194
  Parameters
146
195
  ----------
@@ -165,12 +214,12 @@ def to_float(
165
214
  maximum: float | None = None,
166
215
  ) -> float | None:
167
216
  """
168
- Coerce ``value`` to a float with optional fallback and bounds.
217
+ Coerce *value* to a float with optional fallback and bounds.
169
218
 
170
219
  Notes
171
220
  -----
172
221
  For strings, leading/trailing whitespace is ignored. Returns ``None``
173
- when coercion fails and no ``default`` is provided.
222
+ when coercion fails and no *default* is provided.
174
223
  """
175
224
  return _normalize_number(
176
225
  _coerce_float,
@@ -186,7 +235,7 @@ def to_maximum_float(
186
235
  default: float,
187
236
  ) -> float:
188
237
  """
189
- Return the greater of ``default`` and ``value`` after float coercion.
238
+ Return the greater of *default* and *value* after float coercion.
190
239
 
191
240
  Parameters
192
241
  ----------
@@ -198,7 +247,7 @@ def to_maximum_float(
198
247
  Returns
199
248
  -------
200
249
  float
201
- ``default`` if coercion fails; else ``max(coerced, default)``.
250
+ *default* if coercion fails; else ``max(coerced, default)``.
202
251
  """
203
252
  result = to_float(value, default)
204
253
  return max(_value_or_default(result, default), default)
@@ -209,7 +258,7 @@ def to_minimum_float(
209
258
  default: float,
210
259
  ) -> float:
211
260
  """
212
- Return the lesser of ``default`` and ``value`` after float coercion.
261
+ Return the lesser of *default* and *value* after float coercion.
213
262
 
214
263
  Parameters
215
264
  ----------
@@ -221,7 +270,7 @@ def to_minimum_float(
221
270
  Returns
222
271
  -------
223
272
  float
224
- ``default`` if coercion fails; else ``min(coerced, default)``.
273
+ *default* if coercion fails; else ``min(coerced, default)``.
225
274
  """
226
275
  result = to_float(value, default)
227
276
  return min(_value_or_default(result, default), default)
@@ -257,12 +306,12 @@ def to_int(
257
306
  maximum: int | None = None,
258
307
  ) -> int | None:
259
308
  """
260
- Coerce ``value`` to an integer with optional fallback and bounds.
309
+ Coerce *value* to an integer with optional fallback and bounds.
261
310
 
262
311
  Notes
263
312
  -----
264
313
  For strings, leading/trailing whitespace is ignored. Returns ``None``
265
- when coercion fails and no ``default`` is provided.
314
+ when coercion fails and no *default* is provided.
266
315
  """
267
316
  return _normalize_number(
268
317
  _coerce_int,
@@ -278,7 +327,7 @@ def to_maximum_int(
278
327
  default: int,
279
328
  ) -> int:
280
329
  """
281
- Return the greater of ``default`` and ``value`` after integer coercion.
330
+ Return the greater of *default* and *value* after integer coercion.
282
331
 
283
332
  Parameters
284
333
  ----------
@@ -290,7 +339,7 @@ def to_maximum_int(
290
339
  Returns
291
340
  -------
292
341
  int
293
- ``default`` if coercion fails; else ``max(coerced, default)``.
342
+ *default* if coercion fails; else ``max(coerced, default)``.
294
343
  """
295
344
  result = to_int(value, default)
296
345
  return max(_value_or_default(result, default), default)
@@ -301,7 +350,7 @@ def to_minimum_int(
301
350
  default: int,
302
351
  ) -> int:
303
352
  """
304
- Return the lesser of ``default`` and ``value`` after integer coercion.
353
+ Return the lesser of *default* and *value* after integer coercion.
305
354
 
306
355
  Parameters
307
356
  ----------
@@ -313,7 +362,7 @@ def to_minimum_int(
313
362
  Returns
314
363
  -------
315
364
  int
316
- ``default`` if coercion fails; else ``min(coerced, default)``.
365
+ *default* if coercion fails; else ``min(coerced, default)``.
317
366
  """
318
367
  result = to_int(value, default)
319
368
  return min(_value_or_default(result, default), default)
@@ -326,21 +375,21 @@ def to_positive_int(
326
375
  minimum: int = 1,
327
376
  ) -> int:
328
377
  """
329
- Return a positive integer, falling back to ``minimum`` when needed.
378
+ Return a positive integer, falling back to *minimum* when needed.
330
379
 
331
380
  Parameters
332
381
  ----------
333
382
  value : Any
334
383
  Candidate input coerced with :func:`to_int`.
335
384
  default : int
336
- Fallback value when coercion fails; clamped by ``minimum``.
385
+ Fallback value when coercion fails; clamped by *minimum*.
337
386
  minimum : int
338
387
  Inclusive lower bound for the result. Defaults to ``1``.
339
388
 
340
389
  Returns
341
390
  -------
342
391
  int
343
- Positive integer respecting ``minimum``.
392
+ Positive integer respecting *minimum*.
344
393
  """
345
394
  result = to_int(value, default, minimum=minimum)
346
395
  return _value_or_default(result, minimum)
@@ -353,7 +402,7 @@ def to_number(
353
402
  value: object,
354
403
  ) -> float | None:
355
404
  """
356
- Coerce ``value`` to a ``float`` using the internal float coercer.
405
+ Coerce *value* to a ``float`` using the internal float coercer.
357
406
 
358
407
  Parameters
359
408
  ----------
@@ -372,7 +421,7 @@ def to_number(
372
421
  # -- Text Processing -- #
373
422
 
374
423
 
375
- def normalized_str(
424
+ def normalize_str(
376
425
  value: str | None,
377
426
  ) -> str:
378
427
  """
@@ -392,6 +441,36 @@ def normalized_str(
392
441
  return (value or '').strip().lower()
393
442
 
394
443
 
444
+ def normalize_choice(
445
+ value: str | None,
446
+ *,
447
+ mapping: Mapping[str, str],
448
+ default: str,
449
+ normalize: Callable[[str | None], str] = normalize_str,
450
+ ) -> str:
451
+ """
452
+ Normalize a string choice using a mapping and fallback.
453
+
454
+ Parameters
455
+ ----------
456
+ value : str | None
457
+ Input value to normalize.
458
+ mapping : Mapping[str, str]
459
+ Mapping of acceptable normalized inputs to output values.
460
+ default : str
461
+ Default return value when input is missing or unrecognized.
462
+ normalize : Callable[[str | None], str], optional
463
+ Normalization function applied to *value*. Defaults to
464
+ :func:`normalize_str`.
465
+
466
+ Returns
467
+ -------
468
+ str
469
+ Normalized mapped value or *default*.
470
+ """
471
+ return mapping.get(normalize(value), default)
472
+
473
+
395
474
  # SECTION: INTERNAL FUNCTIONS =============================================== #
396
475
 
397
476
 
@@ -401,7 +480,7 @@ def _clamp(
401
480
  maximum: Num | None,
402
481
  ) -> Num:
403
482
  """
404
- Return ``value`` constrained to the interval ``[minimum, maximum]``.
483
+ Return *value* constrained to the interval ``[minimum, maximum]``.
405
484
 
406
485
  Parameters
407
486
  ----------
@@ -425,6 +504,61 @@ def _clamp(
425
504
  return value
426
505
 
427
506
 
507
+ def _prepare_substitutions(
508
+ vars_map: StrAnyMap | None,
509
+ env_map: Mapping[str, Any] | None,
510
+ ) -> tuple[tuple[str, Any], ...]:
511
+ """
512
+ Merge variable and environment maps into an ordered substitutions list.
513
+
514
+ Parameters
515
+ ----------
516
+ vars_map : StrAnyMap | None
517
+ Mapping of variable names to replacement values (lower precedence).
518
+ env_map : Mapping[str, Any] | None
519
+ Environment-backed values that override entries from *vars_map*.
520
+
521
+ Returns
522
+ -------
523
+ tuple[tuple[str, Any], ...]
524
+ Immutable sequence of ``(name, value)`` pairs suitable for token
525
+ replacement.
526
+ """
527
+ if not vars_map and not env_map:
528
+ return ()
529
+ merged: dict[str, Any] = {**(vars_map or {}), **(env_map or {})}
530
+ return tuple(merged.items())
531
+
532
+
533
+ def _replace_tokens(
534
+ text: str,
535
+ substitutions: Iterable[tuple[str, Any]],
536
+ ) -> str:
537
+ """
538
+ Replace ``${VAR}`` tokens in *text* using *substitutions*.
539
+
540
+ Parameters
541
+ ----------
542
+ text : str
543
+ Input string that may contain ``${VAR}`` tokens.
544
+ substitutions : Iterable[tuple[str, Any]]
545
+ Sequence of ``(name, value)`` pairs used for token replacement.
546
+
547
+ Returns
548
+ -------
549
+ str
550
+ Updated text with replacements applied.
551
+ """
552
+ if not substitutions:
553
+ return text
554
+ out = text
555
+ for name, replacement in substitutions:
556
+ token = f'${{{name}}}'
557
+ if token in out:
558
+ out = out.replace(token, str(replacement))
559
+ return out
560
+
561
+
428
562
  def _coerce_float(
429
563
  value: object,
430
564
  ) -> float | None:
@@ -502,7 +636,7 @@ def _integral_from_float(
502
636
  candidate: float | None,
503
637
  ) -> int | None:
504
638
  """
505
- Return ``int(candidate)`` when ``candidate`` is integral.
639
+ Return ``int(candidate)`` when *candidate* is integral.
506
640
 
507
641
  Parameters
508
642
  ----------
@@ -512,7 +646,7 @@ def _integral_from_float(
512
646
  Returns
513
647
  -------
514
648
  int | None
515
- Integer form of ``candidate``; else ``None`` if not integral.
649
+ Integer form of *candidate*; else ``None`` if not integral.
516
650
  """
517
651
  if candidate is None or not candidate.is_integer():
518
652
  return None
@@ -528,7 +662,7 @@ def _normalize_number(
528
662
  maximum: Num | None = None,
529
663
  ) -> Num | None:
530
664
  """
531
- Coerce ``value`` with ``coercer`` and optionally clamp it.
665
+ Coerce *value* with *coercer* and optionally clamp it.
532
666
 
533
667
  Parameters
534
668
  ----------
@@ -561,7 +695,7 @@ def _validate_bounds(
561
695
  maximum: Num | None,
562
696
  ) -> tuple[Num | None, Num | None]:
563
697
  """
564
- Ensure ``minimum`` does not exceed ``maximum``.
698
+ Ensure *minimum* does not exceed *maximum*.
565
699
 
566
700
  Parameters
567
701
  ----------
@@ -590,7 +724,7 @@ def _value_or_default(
590
724
  default: Num,
591
725
  ) -> Num:
592
726
  """
593
- Return ``value`` if not ``None``; else ``default``.
727
+ Return *value* if not ``None``; else *default*.
594
728
 
595
729
  Parameters
596
730
  ----------
@@ -602,6 +736,6 @@ def _value_or_default(
602
736
  Returns
603
737
  -------
604
738
  Num
605
- ``value`` or ``default``.
739
+ *value* or *default*.
606
740
  """
607
741
  return default if value is None else value
@@ -1,7 +1,7 @@
1
- # etlplus.config subpackage
1
+ # `etlplus.workflow` Subpackage
2
2
 
3
- Documentation for the `etlplus.config` subpackage: configuration helpers for connectors, pipelines,
4
- jobs, and profiles.
3
+ Documentation for the `etlplus.workflow` subpackage: configuration helpers for connectors,
4
+ pipelines, jobs, and profiles.
5
5
 
6
6
  - Provides classes and utilities for managing ETL pipeline configuration
7
7
  - Supports YAML/JSON config loading and validation
@@ -10,7 +10,7 @@ jobs, and profiles.
10
10
 
11
11
  Back to project overview: see the top-level [README](../../README.md).
12
12
 
13
- - [etlplus.config subpackage](#etlplusconfig-subpackage)
13
+ - [`etlplus.workflow` Subpackage](#etlplusworkflow-subpackage)
14
14
  - [Supported Configuration Types](#supported-configuration-types)
15
15
  - [Loading and Validating Configs](#loading-and-validating-configs)
16
16
  - [Example: Loading a Pipeline Config](#example-loading-a-pipeline-config)
@@ -28,7 +28,7 @@ Back to project overview: see the top-level [README](../../README.md).
28
28
  Use the provided classes to load and validate configuration files:
29
29
 
30
30
  ```python
31
- from etlplus.config import PipelineConfig
31
+ from etlplus.workflow import PipelineConfig
32
32
 
33
33
  cfg = PipelineConfig.from_yaml("pipeline.yml")
34
34
  ```
@@ -39,7 +39,7 @@ cfg = PipelineConfig.from_yaml("pipeline.yml")
39
39
  ## Example: Loading a Pipeline Config
40
40
 
41
41
  ```python
42
- from etlplus.config import PipelineConfig
42
+ from etlplus.workflow import PipelineConfig
43
43
 
44
44
  pipeline = PipelineConfig.from_yaml("configs/pipeline.yml")
45
45
  print(pipeline)
@@ -0,0 +1,32 @@
1
+ """
2
+ :mod:`etlplus.workflow` package.
3
+
4
+ Job workflow helpers.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from .dag import topological_sort_jobs
10
+ from .jobs import ExtractRef
11
+ from .jobs import JobConfig
12
+ from .jobs import LoadRef
13
+ from .jobs import TransformRef
14
+ from .jobs import ValidationRef
15
+ from .pipeline import PipelineConfig
16
+ from .pipeline import load_pipeline_config
17
+
18
+ # SECTION: EXPORTS ========================================================== #
19
+
20
+
21
+ __all__ = [
22
+ # Data Classes
23
+ 'ExtractRef',
24
+ 'JobConfig',
25
+ 'LoadRef',
26
+ 'PipelineConfig',
27
+ 'TransformRef',
28
+ 'ValidationRef',
29
+ # Functions
30
+ 'load_pipeline_config',
31
+ 'topological_sort_jobs',
32
+ ]
@@ -1,8 +1,8 @@
1
1
  """
2
- :mod:`etlplus.dag` module.
2
+ :mod:`etlplus.workflow.dag` module.
3
3
 
4
4
  Lightweight directed acyclic graph (DAG) helpers for ordering jobs based on
5
- ``depends_on``.
5
+ :attr:`depends_on`.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -10,13 +10,15 @@ from __future__ import annotations
10
10
  from collections import deque
11
11
  from dataclasses import dataclass
12
12
 
13
- from .config.jobs import JobConfig
13
+ from .jobs import JobConfig
14
14
 
15
15
  # SECTION: EXPORTS ========================================================== #
16
16
 
17
17
 
18
18
  __all__ = [
19
+ # Errors
19
20
  'DagError',
21
+ # Functions
20
22
  'topological_sort_jobs',
21
23
  ]
22
24
 
@@ -52,7 +54,7 @@ def topological_sort_jobs(
52
54
  jobs: list[JobConfig],
53
55
  ) -> list[JobConfig]:
54
56
  """
55
- Return jobs in topological order based on ``depends_on``.
57
+ Return jobs in topological order based on :attr:`depends_on`.
56
58
 
57
59
  Parameters
58
60
  ----------