etlplus 0.14.3__py3-none-any.whl → 0.15.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.
- etlplus/README.md +1 -1
- etlplus/api/README.md +2 -2
- etlplus/api/utils.py +5 -1
- etlplus/cli/README.md +2 -2
- etlplus/cli/handlers.py +2 -2
- etlplus/config/README.md +31 -33
- etlplus/config/__init__.py +9 -32
- etlplus/config/types.py +0 -64
- etlplus/database/README.md +2 -2
- etlplus/file/README.md +2 -2
- etlplus/ops/run.py +14 -9
- etlplus/templates/README.md +2 -2
- etlplus/workflow/README.md +52 -0
- etlplus/workflow/__init__.py +43 -0
- etlplus/{config → workflow}/connector.py +17 -16
- etlplus/workflow/dag.py +105 -0
- etlplus/{config → workflow}/jobs.py +17 -11
- etlplus/{config → workflow}/pipeline.py +11 -3
- etlplus/{config → workflow}/profile.py +8 -5
- etlplus/workflow/types.py +115 -0
- {etlplus-0.14.3.dist-info → etlplus-0.15.0.dist-info}/METADATA +1 -1
- {etlplus-0.14.3.dist-info → etlplus-0.15.0.dist-info}/RECORD +27 -23
- {etlplus-0.14.3.dist-info → etlplus-0.15.0.dist-info}/WHEEL +1 -1
- /etlplus/{config → workflow}/utils.py +0 -0
- {etlplus-0.14.3.dist-info → etlplus-0.15.0.dist-info}/entry_points.txt +0 -0
- {etlplus-0.14.3.dist-info → etlplus-0.15.0.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.14.3.dist-info → etlplus-0.15.0.dist-info}/top_level.txt +0 -0
etlplus/README.md
CHANGED
etlplus/api/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# etlplus.api
|
|
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
|
|
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)
|
etlplus/api/utils.py
CHANGED
|
@@ -892,4 +892,8 @@ def resolve_request(
|
|
|
892
892
|
'Session object must supply a callable '
|
|
893
893
|
f'"{http_method.value}" method',
|
|
894
894
|
)
|
|
895
|
-
|
|
895
|
+
typed_request_callable = cast(
|
|
896
|
+
Callable[..., requests.Response],
|
|
897
|
+
request_callable,
|
|
898
|
+
)
|
|
899
|
+
return typed_request_callable, request_timeout, http_method
|
etlplus/cli/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# etlplus.cli
|
|
1
|
+
# `etlplus.cli` Subpackage
|
|
2
2
|
|
|
3
3
|
Documentation for the `etlplus.cli` subpackage: command-line interface for ETLPlus workflows.
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ Documentation for the `etlplus.cli` subpackage: command-line interface for ETLPl
|
|
|
9
9
|
|
|
10
10
|
Back to project overview: see the top-level [README](../../README.md).
|
|
11
11
|
|
|
12
|
-
- [etlplus.cli
|
|
12
|
+
- [`etlplus.cli` Subpackage](#etlpluscli-subpackage)
|
|
13
13
|
- [Available Commands](#available-commands)
|
|
14
14
|
- [Command Options](#command-options)
|
|
15
15
|
- [Example: Running a Pipeline](#example-running-a-pipeline)
|
etlplus/cli/handlers.py
CHANGED
|
@@ -14,8 +14,6 @@ from typing import Any
|
|
|
14
14
|
from typing import Literal
|
|
15
15
|
from typing import cast
|
|
16
16
|
|
|
17
|
-
from ..config import PipelineConfig
|
|
18
|
-
from ..config import load_pipeline_config
|
|
19
17
|
from ..database import load_table_spec
|
|
20
18
|
from ..database import render_tables
|
|
21
19
|
from ..file import File
|
|
@@ -28,6 +26,8 @@ from ..ops import validate
|
|
|
28
26
|
from ..ops.validate import FieldRules
|
|
29
27
|
from ..types import JSONData
|
|
30
28
|
from ..types import TemplateKey
|
|
29
|
+
from ..workflow import PipelineConfig
|
|
30
|
+
from ..workflow import load_pipeline_config
|
|
31
31
|
from . import io as cli_io
|
|
32
32
|
|
|
33
33
|
# SECTION: EXPORTS ========================================================== #
|
etlplus/config/README.md
CHANGED
|
@@ -1,52 +1,50 @@
|
|
|
1
|
-
# etlplus.config
|
|
1
|
+
# `etlplus.config` Subpackage
|
|
2
2
|
|
|
3
|
-
Documentation for the `etlplus.config` subpackage:
|
|
4
|
-
|
|
3
|
+
Documentation for the `etlplus.config` subpackage: type definitions and config shape helpers for
|
|
4
|
+
ETLPlus.
|
|
5
5
|
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
- Exposes type definitions for config schemas
|
|
6
|
+
- Exposes TypedDict-based config schemas for API profiles and endpoints
|
|
7
|
+
- Provides exported type aliases for API configuration maps
|
|
8
|
+
- Designed for Python 3.13 typing and editor assistance (runtime parsing lives elsewhere)
|
|
10
9
|
|
|
11
10
|
Back to project overview: see the top-level [README](../../README.md).
|
|
12
11
|
|
|
13
|
-
- [etlplus.config
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [Example:
|
|
12
|
+
- [`etlplus.config` Subpackage](#etlplusconfig-subpackage)
|
|
13
|
+
- [Modules](#modules)
|
|
14
|
+
- [Exported Types](#exported-types)
|
|
15
|
+
- [Example: Typing an API Config](#example-typing-an-api-config)
|
|
17
16
|
- [See Also](#see-also)
|
|
18
17
|
|
|
19
|
-
##
|
|
18
|
+
## Modules
|
|
20
19
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
- **Pipeline**: End-to-end pipeline configuration
|
|
24
|
-
- **Profile**: User or environment-specific settings
|
|
20
|
+
- `etlplus.config.__init__`: package exports and high-level package notes
|
|
21
|
+
- `etlplus.config.types`: TypedDict-based config schemas
|
|
25
22
|
|
|
26
|
-
##
|
|
23
|
+
## Exported Types
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
- `ApiConfigMap`: top-level API config shape
|
|
26
|
+
- `ApiProfileConfigMap`: per-profile API config shape
|
|
27
|
+
- `ApiProfileDefaultsMap`: defaults block within a profile
|
|
28
|
+
- `EndpointMap`: endpoint config shape
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
from etlplus.config import PipelineConfig
|
|
32
|
-
|
|
33
|
-
cfg = PipelineConfig.from_yaml("pipeline.yml")
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
- Supports YAML and JSON formats
|
|
37
|
-
- Validates against expected schema
|
|
38
|
-
|
|
39
|
-
## Example: Loading a Pipeline Config
|
|
30
|
+
## Example: Typing an API Config
|
|
40
31
|
|
|
41
32
|
```python
|
|
42
|
-
from etlplus.config import
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
from etlplus.config import ApiConfigMap
|
|
34
|
+
|
|
35
|
+
api_cfg: ApiConfigMap = {
|
|
36
|
+
"base_url": "https://example.test",
|
|
37
|
+
"headers": {"Authorization": "Bearer token"},
|
|
38
|
+
"endpoints": {
|
|
39
|
+
"users": {
|
|
40
|
+
"path": "/users",
|
|
41
|
+
"method": "GET",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}
|
|
46
45
|
```
|
|
47
46
|
|
|
48
47
|
## See Also
|
|
49
48
|
|
|
50
49
|
- Top-level CLI and library usage in the main [README](../../README.md)
|
|
51
50
|
- Config type definitions in [types.py](types.py)
|
|
52
|
-
- Config utilities in [utils.py](utils.py)
|
etlplus/config/__init__.py
CHANGED
|
@@ -16,41 +16,18 @@ Notes
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from .
|
|
20
|
-
from .
|
|
21
|
-
from .
|
|
22
|
-
from .
|
|
23
|
-
from .connector import parse_connector
|
|
24
|
-
from .jobs import ExtractRef
|
|
25
|
-
from .jobs import JobConfig
|
|
26
|
-
from .jobs import LoadRef
|
|
27
|
-
from .jobs import TransformRef
|
|
28
|
-
from .jobs import ValidationRef
|
|
29
|
-
from .pipeline import PipelineConfig
|
|
30
|
-
from .pipeline import load_pipeline_config
|
|
31
|
-
from .profile import ProfileConfig
|
|
32
|
-
from .types import ConnectorType
|
|
19
|
+
from .types import ApiConfigMap
|
|
20
|
+
from .types import ApiProfileConfigMap
|
|
21
|
+
from .types import ApiProfileDefaultsMap
|
|
22
|
+
from .types import EndpointMap
|
|
33
23
|
|
|
34
24
|
# SECTION: EXPORTS ========================================================== #
|
|
35
25
|
|
|
36
26
|
|
|
37
27
|
__all__ = [
|
|
38
|
-
#
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'ConnectorFile',
|
|
44
|
-
'parse_connector',
|
|
45
|
-
# Jobs / Refs
|
|
46
|
-
'ExtractRef',
|
|
47
|
-
'JobConfig',
|
|
48
|
-
'LoadRef',
|
|
49
|
-
'TransformRef',
|
|
50
|
-
'ValidationRef',
|
|
51
|
-
# Pipeline
|
|
52
|
-
'PipelineConfig',
|
|
53
|
-
'load_pipeline_config',
|
|
54
|
-
# Profile
|
|
55
|
-
'ProfileConfig',
|
|
28
|
+
# Typed Dicts
|
|
29
|
+
'ApiConfigMap',
|
|
30
|
+
'ApiProfileConfigMap',
|
|
31
|
+
'ApiProfileDefaultsMap',
|
|
32
|
+
'EndpointMap',
|
|
56
33
|
]
|
etlplus/config/types.py
CHANGED
|
@@ -33,7 +33,6 @@ from __future__ import annotations
|
|
|
33
33
|
|
|
34
34
|
from collections.abc import Mapping
|
|
35
35
|
from typing import Any
|
|
36
|
-
from typing import Literal
|
|
37
36
|
from typing import TypedDict
|
|
38
37
|
|
|
39
38
|
from ..api import PaginationConfigMap
|
|
@@ -44,26 +43,17 @@ from ..types import StrAnyMap
|
|
|
44
43
|
|
|
45
44
|
|
|
46
45
|
__all__ = [
|
|
47
|
-
# Type aliases
|
|
48
|
-
'ConnectorType',
|
|
49
|
-
# 'PaginationType',
|
|
50
46
|
# TypedDicts
|
|
51
47
|
'ApiProfileDefaultsMap',
|
|
52
48
|
'ApiProfileConfigMap',
|
|
53
49
|
'ApiConfigMap',
|
|
54
50
|
'EndpointMap',
|
|
55
|
-
'ConnectorApiConfigMap',
|
|
56
|
-
'ConnectorDbConfigMap',
|
|
57
|
-
'ConnectorFileConfigMap',
|
|
58
51
|
]
|
|
59
52
|
|
|
60
53
|
|
|
61
54
|
# SECTION: TYPE ALIASES ===================================================== #
|
|
62
55
|
|
|
63
56
|
|
|
64
|
-
# Literal type for supported connector kinds
|
|
65
|
-
type ConnectorType = Literal['api', 'database', 'file']
|
|
66
|
-
|
|
67
57
|
# Literal type for supported pagination kinds
|
|
68
58
|
# type PaginationType = Literal['page', 'offset', 'cursor']
|
|
69
59
|
|
|
@@ -129,60 +119,6 @@ class ApiProfileDefaultsMap(TypedDict, total=False):
|
|
|
129
119
|
rate_limit: RateLimitConfigMap | StrAnyMap
|
|
130
120
|
|
|
131
121
|
|
|
132
|
-
class ConnectorApiConfigMap(TypedDict, total=False):
|
|
133
|
-
"""
|
|
134
|
-
Shape accepted by ConnectorApi.from_obj (all keys optional).
|
|
135
|
-
|
|
136
|
-
See Also
|
|
137
|
-
--------
|
|
138
|
-
- etlplus.config.connector.ConnectorApi.from_obj
|
|
139
|
-
"""
|
|
140
|
-
|
|
141
|
-
name: str
|
|
142
|
-
type: ConnectorType
|
|
143
|
-
url: str
|
|
144
|
-
method: str
|
|
145
|
-
headers: StrAnyMap
|
|
146
|
-
query_params: StrAnyMap
|
|
147
|
-
pagination: PaginationConfigMap
|
|
148
|
-
rate_limit: RateLimitConfigMap
|
|
149
|
-
api: str
|
|
150
|
-
endpoint: str
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
class ConnectorDbConfigMap(TypedDict, total=False):
|
|
154
|
-
"""
|
|
155
|
-
Shape accepted by ConnectorDb.from_obj (all keys optional).
|
|
156
|
-
|
|
157
|
-
See Also
|
|
158
|
-
--------
|
|
159
|
-
- etlplus.config.connector.ConnectorDb.from_obj
|
|
160
|
-
"""
|
|
161
|
-
|
|
162
|
-
name: str
|
|
163
|
-
type: ConnectorType
|
|
164
|
-
connection_string: str
|
|
165
|
-
query: str
|
|
166
|
-
table: str
|
|
167
|
-
mode: str
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
class ConnectorFileConfigMap(TypedDict, total=False):
|
|
171
|
-
"""
|
|
172
|
-
Shape accepted by ConnectorFile.from_obj (all keys optional).
|
|
173
|
-
|
|
174
|
-
See Also
|
|
175
|
-
--------
|
|
176
|
-
- etlplus.config.connector.ConnectorFile.from_obj
|
|
177
|
-
"""
|
|
178
|
-
|
|
179
|
-
name: str
|
|
180
|
-
type: ConnectorType
|
|
181
|
-
format: str
|
|
182
|
-
path: str
|
|
183
|
-
options: StrAnyMap
|
|
184
|
-
|
|
185
|
-
|
|
186
122
|
class EndpointMap(TypedDict, total=False):
|
|
187
123
|
"""
|
|
188
124
|
Shape accepted by EndpointConfig.from_obj.
|
etlplus/database/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# etlplus.database
|
|
1
|
+
# `etlplus.database` Subpackage
|
|
2
2
|
|
|
3
3
|
Documentation for the `etlplus.database` subpackage: database engine, schema, and ORM helpers.
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ Documentation for the `etlplus.database` subpackage: database engine, schema, an
|
|
|
9
9
|
|
|
10
10
|
Back to project overview: see the top-level [README](../../README.md).
|
|
11
11
|
|
|
12
|
-
- [etlplus.database
|
|
12
|
+
- [`etlplus.database` Subpackage](#etlplusdatabase-subpackage)
|
|
13
13
|
- [Database Engine and Connections](#database-engine-and-connections)
|
|
14
14
|
- [Schema and DDL Helpers](#schema-and-ddl-helpers)
|
|
15
15
|
- [ORM Utilities](#orm-utilities)
|
etlplus/file/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# etlplus.file
|
|
1
|
+
# `etlplus.file` Subpackage
|
|
2
2
|
|
|
3
3
|
Documentation for the `etlplus.file` subpackage: unified file format support and helpers for reading
|
|
4
4
|
and writing data files.
|
|
@@ -11,7 +11,7 @@ and writing data files.
|
|
|
11
11
|
|
|
12
12
|
Back to project overview: see the top-level [README](../../README.md).
|
|
13
13
|
|
|
14
|
-
- [etlplus.file
|
|
14
|
+
- [`etlplus.file` Subpackage](#etlplusfile-subpackage)
|
|
15
15
|
- [Supported File Formats](#supported-file-formats)
|
|
16
16
|
- [Inferring File Format and Compression](#inferring-file-format-and-compression)
|
|
17
17
|
- [Reading and Writing Files](#reading-and-writing-files)
|
etlplus/ops/run.py
CHANGED
|
@@ -20,7 +20,6 @@ from ..api import RequestOptions
|
|
|
20
20
|
from ..api import compose_api_request_env
|
|
21
21
|
from ..api import compose_api_target_env
|
|
22
22
|
from ..api import paginate_with_client
|
|
23
|
-
from ..config import load_pipeline_config
|
|
24
23
|
from ..enums import DataConnectorType
|
|
25
24
|
from ..file import FileFormat
|
|
26
25
|
from ..types import JSONData
|
|
@@ -29,6 +28,7 @@ from ..types import PipelineConfig
|
|
|
29
28
|
from ..types import StrPath
|
|
30
29
|
from ..types import Timeout
|
|
31
30
|
from ..utils import print_json
|
|
31
|
+
from ..workflow import load_pipeline_config
|
|
32
32
|
from .extract import extract
|
|
33
33
|
from .load import load
|
|
34
34
|
from .transform import transform
|
|
@@ -162,9 +162,12 @@ def run(
|
|
|
162
162
|
# can monkeypatch this class on etlplus.ops.run.
|
|
163
163
|
ClientClass = EndpointClient # noqa: N806
|
|
164
164
|
client = ClientClass(
|
|
165
|
-
base_url=cast(str, env
|
|
165
|
+
base_url=cast(str, env.get('base_url')),
|
|
166
166
|
base_path=cast(str | None, env.get('base_path')),
|
|
167
|
-
endpoints=cast(
|
|
167
|
+
endpoints=cast(
|
|
168
|
+
dict[str, str],
|
|
169
|
+
env.get('endpoints_map', {}),
|
|
170
|
+
),
|
|
168
171
|
retry=env.get('retry'),
|
|
169
172
|
retry_network_errors=bool(
|
|
170
173
|
env.get('retry_network_errors', False),
|
|
@@ -173,7 +176,7 @@ def run(
|
|
|
173
176
|
)
|
|
174
177
|
data = paginate_with_client(
|
|
175
178
|
client,
|
|
176
|
-
cast(str, env
|
|
179
|
+
cast(str, env.get('endpoint_key')),
|
|
177
180
|
env.get('params'),
|
|
178
181
|
env.get('headers'),
|
|
179
182
|
env.get('timeout'),
|
|
@@ -276,12 +279,14 @@ def run(
|
|
|
276
279
|
if not url_t:
|
|
277
280
|
raise ValueError('API target missing "url"')
|
|
278
281
|
kwargs_t: dict[str, Any] = {}
|
|
279
|
-
|
|
280
|
-
|
|
282
|
+
headers = env_t.get('headers')
|
|
283
|
+
if headers:
|
|
284
|
+
kwargs_t['headers'] = cast(dict[str, str], headers)
|
|
281
285
|
if env_t.get('timeout') is not None:
|
|
282
|
-
kwargs_t['timeout'] = env_t
|
|
283
|
-
|
|
284
|
-
|
|
286
|
+
kwargs_t['timeout'] = env_t.get('timeout')
|
|
287
|
+
session = env_t.get('session')
|
|
288
|
+
if session is not None:
|
|
289
|
+
kwargs_t['session'] = session
|
|
285
290
|
result = load(
|
|
286
291
|
data,
|
|
287
292
|
'api',
|
etlplus/templates/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# etlplus.templates
|
|
1
|
+
# `etlplus.templates` Subpackage
|
|
2
2
|
|
|
3
3
|
Documentation for the `etlplus.templates` subpackage: SQL and DDL template helpers.
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ Documentation for the `etlplus.templates` subpackage: SQL and DDL template helpe
|
|
|
8
8
|
|
|
9
9
|
Back to project overview: see the top-level [README](../../README.md).
|
|
10
10
|
|
|
11
|
-
- [etlplus.templates
|
|
11
|
+
- [`etlplus.templates` Subpackage](#etlplus-templates-subpackage)
|
|
12
12
|
- [Available Templates](#available-templates)
|
|
13
13
|
- [Rendering Templates](#rendering-templates)
|
|
14
14
|
- [Example: Rendering a DDL Template](#example-rendering-a-ddl-template)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `etlplus.workflow` Subpackage
|
|
2
|
+
|
|
3
|
+
Documentation for the `etlplus.workflow` subpackage: configuration helpers for connectors,
|
|
4
|
+
pipelines, jobs, and profiles.
|
|
5
|
+
|
|
6
|
+
- Provides classes and utilities for managing ETL pipeline configuration
|
|
7
|
+
- Supports YAML/JSON config loading and validation
|
|
8
|
+
- Includes helpers for connectors, jobs, pipelines, and profiles
|
|
9
|
+
- Exposes type definitions for config schemas
|
|
10
|
+
|
|
11
|
+
Back to project overview: see the top-level [README](../../README.md).
|
|
12
|
+
|
|
13
|
+
- [`etlplus.workflow` Subpackage](#etlplusworkflow-subpackage)
|
|
14
|
+
- [Supported Configuration Types](#supported-configuration-types)
|
|
15
|
+
- [Loading and Validating Configs](#loading-and-validating-configs)
|
|
16
|
+
- [Example: Loading a Pipeline Config](#example-loading-a-pipeline-config)
|
|
17
|
+
- [See Also](#see-also)
|
|
18
|
+
|
|
19
|
+
## Supported Configuration Types
|
|
20
|
+
|
|
21
|
+
- **Connector**: Connection details for databases, files, or APIs
|
|
22
|
+
- **Job**: ETL job definitions and scheduling
|
|
23
|
+
- **Pipeline**: End-to-end pipeline configuration
|
|
24
|
+
- **Profile**: User or environment-specific settings
|
|
25
|
+
|
|
26
|
+
## Loading and Validating Configs
|
|
27
|
+
|
|
28
|
+
Use the provided classes to load and validate configuration files:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from etlplus.workflow import PipelineConfig
|
|
32
|
+
|
|
33
|
+
cfg = PipelineConfig.from_yaml("pipeline.yml")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- Supports YAML and JSON formats
|
|
37
|
+
- Validates against expected schema
|
|
38
|
+
|
|
39
|
+
## Example: Loading a Pipeline Config
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from etlplus.workflow import PipelineConfig
|
|
43
|
+
|
|
44
|
+
pipeline = PipelineConfig.from_yaml("configs/pipeline.yml")
|
|
45
|
+
print(pipeline)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## See Also
|
|
49
|
+
|
|
50
|
+
- Top-level CLI and library usage in the main [README](../../README.md)
|
|
51
|
+
- Config type definitions in [types.py](types.py)
|
|
52
|
+
- Config utilities in [utils.py](utils.py)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus.workflow` package.
|
|
3
|
+
|
|
4
|
+
Job workflow helpers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from .connector import Connector
|
|
10
|
+
from .connector import ConnectorApi
|
|
11
|
+
from .connector import ConnectorDb
|
|
12
|
+
from .connector import ConnectorFile
|
|
13
|
+
from .connector import parse_connector
|
|
14
|
+
from .dag import topological_sort_jobs
|
|
15
|
+
from .jobs import ExtractRef
|
|
16
|
+
from .jobs import JobConfig
|
|
17
|
+
from .jobs import LoadRef
|
|
18
|
+
from .jobs import TransformRef
|
|
19
|
+
from .jobs import ValidationRef
|
|
20
|
+
from .pipeline import PipelineConfig
|
|
21
|
+
from .pipeline import load_pipeline_config
|
|
22
|
+
|
|
23
|
+
# SECTION: EXPORTS ========================================================== #
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# Data Classes
|
|
28
|
+
'ConnectorApi',
|
|
29
|
+
'ConnectorDb',
|
|
30
|
+
'ConnectorFile',
|
|
31
|
+
'ExtractRef',
|
|
32
|
+
'JobConfig',
|
|
33
|
+
'LoadRef',
|
|
34
|
+
'PipelineConfig',
|
|
35
|
+
'TransformRef',
|
|
36
|
+
'ValidationRef',
|
|
37
|
+
# Functions
|
|
38
|
+
'load_pipeline_config',
|
|
39
|
+
'parse_connector',
|
|
40
|
+
'topological_sort_jobs',
|
|
41
|
+
# Type Aliases
|
|
42
|
+
'Connector',
|
|
43
|
+
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
2
|
+
:mod:`etlplus.workflow.connector` module.
|
|
3
3
|
|
|
4
4
|
A module defining configuration types for data source/target connectors in ETL
|
|
5
5
|
pipelines. A "connector" is any I/O endpoint:
|
|
@@ -11,18 +11,19 @@ pipelines. A "connector" is any I/O endpoint:
|
|
|
11
11
|
|
|
12
12
|
Examples
|
|
13
13
|
--------
|
|
14
|
-
- Use
|
|
15
|
-
|
|
16
|
-
- Use the
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
- Use :class:`ConnectorApi`/:class:`ConnectorFile`/:class:`ConnectorDb` when
|
|
15
|
+
you want the concrete dataclasses.
|
|
16
|
+
- Use the :class:`Connector` union for typing a value that can be any
|
|
17
|
+
connector.
|
|
18
|
+
- Use :func:`parse_connector(obj)` to construct a connector instance from a
|
|
19
|
+
generic mapping that includes a *type* key.
|
|
19
20
|
|
|
20
21
|
Notes
|
|
21
22
|
-----
|
|
22
23
|
- TypedDict shapes are editor hints; runtime parsing remains permissive
|
|
23
|
-
|
|
24
|
+
(from_obj accepts Mapping[str, Any]).
|
|
24
25
|
- TypedDicts referenced in :mod:`etlplus.config.types` remain editor hints.
|
|
25
|
-
|
|
26
|
+
Runtime parsing stays permissive and tolerant.
|
|
26
27
|
|
|
27
28
|
See Also
|
|
28
29
|
--------
|
|
@@ -59,7 +60,7 @@ if TYPE_CHECKING: # Editor-only typing hints to avoid runtime imports
|
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
__all__ = [
|
|
62
|
-
# Classes
|
|
63
|
+
# Data Classes
|
|
63
64
|
'ConnectorApi',
|
|
64
65
|
'ConnectorDb',
|
|
65
66
|
'ConnectorFile',
|
|
@@ -83,12 +84,12 @@ class ConnectorApi:
|
|
|
83
84
|
name : str
|
|
84
85
|
Unique connector name.
|
|
85
86
|
type : ConnectorType
|
|
86
|
-
Connector kind literal, always ``
|
|
87
|
+
Connector kind literal, always ``'api'``.
|
|
87
88
|
url : str | None
|
|
88
89
|
Direct absolute URL (when not using ``service``/``endpoint`` refs).
|
|
89
90
|
method : str | None
|
|
90
91
|
Optional HTTP method; typically omitted for sources (defaults to
|
|
91
|
-
GET) and used for targets (e.g., ``
|
|
92
|
+
GET) and used for targets (e.g., ``'post'``).
|
|
92
93
|
headers : dict[str, str]
|
|
93
94
|
Additional request headers.
|
|
94
95
|
query_params : dict[str, Any]
|
|
@@ -111,7 +112,7 @@ class ConnectorApi:
|
|
|
111
112
|
|
|
112
113
|
# Direct form
|
|
113
114
|
url: str | None = None
|
|
114
|
-
# Optional HTTP method; typically omitted for sources (defaults to GET
|
|
115
|
+
# Optional HTTP method; typically omitted for sources (defaults to GET)
|
|
115
116
|
# at runtime) and used for targets (e.g., 'post', 'put').
|
|
116
117
|
method: str | None = None
|
|
117
118
|
headers: dict[str, str] = field(default_factory=dict)
|
|
@@ -185,7 +186,7 @@ class ConnectorDb:
|
|
|
185
186
|
name : str
|
|
186
187
|
Unique connector name.
|
|
187
188
|
type : ConnectorType
|
|
188
|
-
Connector kind literal, always ``
|
|
189
|
+
Connector kind literal, always ``'database'``.
|
|
189
190
|
connection_string : str | None
|
|
190
191
|
Connection string/DSN for the database.
|
|
191
192
|
query : str | None
|
|
@@ -193,7 +194,7 @@ class ConnectorDb:
|
|
|
193
194
|
table : str | None
|
|
194
195
|
Target/source table name (optional).
|
|
195
196
|
mode : str | None
|
|
196
|
-
Load mode hint (e.g., ``
|
|
197
|
+
Load mode hint (e.g., ``'append'``, ``'replace'``) — future use.
|
|
197
198
|
"""
|
|
198
199
|
|
|
199
200
|
# -- Attributes -- #
|
|
@@ -262,9 +263,9 @@ class ConnectorFile:
|
|
|
262
263
|
name : str
|
|
263
264
|
Unique connector name.
|
|
264
265
|
type : ConnectorType
|
|
265
|
-
Connector kind literal, always ``
|
|
266
|
+
Connector kind literal, always ``'file'``.
|
|
266
267
|
format : str | None
|
|
267
|
-
File format (e.g., ``
|
|
268
|
+
File format (e.g., ``'json'``, ``'csv'``).
|
|
268
269
|
path : str | None
|
|
269
270
|
File path or URI.
|
|
270
271
|
options : dict[str, Any]
|
etlplus/workflow/dag.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus.workflow.dag` module.
|
|
3
|
+
|
|
4
|
+
Lightweight directed acyclic graph (DAG) helpers for ordering jobs based on
|
|
5
|
+
:attr:`depends_on`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections import deque
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
from .jobs import JobConfig
|
|
14
|
+
|
|
15
|
+
# SECTION: EXPORTS ========================================================== #
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
# Errors
|
|
20
|
+
'DagError',
|
|
21
|
+
# Functions
|
|
22
|
+
'topological_sort_jobs',
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# SECTION: ERRORS =========================================================== #
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(slots=True)
|
|
30
|
+
class DagError(ValueError):
|
|
31
|
+
"""
|
|
32
|
+
Raised when the job dependency graph is invalid.
|
|
33
|
+
|
|
34
|
+
Attributes
|
|
35
|
+
----------
|
|
36
|
+
message : str
|
|
37
|
+
Error message.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# -- Attributes -- #
|
|
41
|
+
|
|
42
|
+
message: str
|
|
43
|
+
|
|
44
|
+
# -- Magic Methods (Object Representation) -- #
|
|
45
|
+
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return self.message
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# SECTION: FUNCTIONS ======================================================== #
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def topological_sort_jobs(
|
|
54
|
+
jobs: list[JobConfig],
|
|
55
|
+
) -> list[JobConfig]:
|
|
56
|
+
"""
|
|
57
|
+
Return jobs in topological order based on :attr:`depends_on`.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
jobs : list[JobConfig]
|
|
62
|
+
List of job configurations to sort.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
list[JobConfig]
|
|
67
|
+
Jobs sorted in topological order.
|
|
68
|
+
|
|
69
|
+
Raises
|
|
70
|
+
------
|
|
71
|
+
DagError
|
|
72
|
+
If a dependency is missing, self-referential, or when a cycle is
|
|
73
|
+
detected.
|
|
74
|
+
"""
|
|
75
|
+
index = {job.name: job for job in jobs}
|
|
76
|
+
edges: dict[str, set[str]] = {name: set() for name in index}
|
|
77
|
+
indegree: dict[str, int] = {name: 0 for name in index}
|
|
78
|
+
|
|
79
|
+
for job in jobs:
|
|
80
|
+
for dep in job.depends_on:
|
|
81
|
+
if dep not in index:
|
|
82
|
+
raise DagError(
|
|
83
|
+
f'Unknown dependency "{dep}" in job "{job.name}"',
|
|
84
|
+
)
|
|
85
|
+
if dep == job.name:
|
|
86
|
+
raise DagError(f'Job "{job.name}" depends on itself')
|
|
87
|
+
if job.name not in edges[dep]:
|
|
88
|
+
edges[dep].add(job.name)
|
|
89
|
+
indegree[job.name] += 1
|
|
90
|
+
|
|
91
|
+
queue = deque(sorted(name for name, deg in indegree.items() if deg == 0))
|
|
92
|
+
ordered: list[str] = []
|
|
93
|
+
|
|
94
|
+
while queue:
|
|
95
|
+
name = queue.popleft()
|
|
96
|
+
ordered.append(name)
|
|
97
|
+
for child in sorted(edges[name]):
|
|
98
|
+
indegree[child] -= 1
|
|
99
|
+
if indegree[child] == 0:
|
|
100
|
+
queue.append(child)
|
|
101
|
+
|
|
102
|
+
if len(ordered) != len(jobs):
|
|
103
|
+
raise DagError('Dependency cycle detected')
|
|
104
|
+
|
|
105
|
+
return [index[name] for name in ordered]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
2
|
+
:mod:`etlplus.workflow.jobs` module.
|
|
3
3
|
|
|
4
4
|
Data classes modeling job orchestration references (extract, validate,
|
|
5
5
|
transform, load).
|
|
6
6
|
|
|
7
7
|
Notes
|
|
8
8
|
-----
|
|
9
|
-
- Lightweight references used inside
|
|
9
|
+
- Lightweight references used inside :class:`PipelineConfig` to avoid storing
|
|
10
10
|
large nested structures.
|
|
11
11
|
- All attributes are simple and optional where appropriate, keeping parsing
|
|
12
12
|
tolerant.
|
|
@@ -26,6 +26,7 @@ from ..utils import maybe_mapping
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
__all__ = [
|
|
29
|
+
# Data Classes
|
|
29
30
|
'ExtractRef',
|
|
30
31
|
'JobConfig',
|
|
31
32
|
'LoadRef',
|
|
@@ -62,12 +63,13 @@ class ExtractRef:
|
|
|
62
63
|
cls,
|
|
63
64
|
obj: Any,
|
|
64
65
|
) -> Self | None:
|
|
65
|
-
"""
|
|
66
|
+
"""
|
|
67
|
+
Parse a mapping into an :class:`ExtractRef` instance.
|
|
66
68
|
|
|
67
69
|
Parameters
|
|
68
70
|
----------
|
|
69
71
|
obj : Any
|
|
70
|
-
Mapping with
|
|
72
|
+
Mapping with :attr:`source` and optional :attr:`options`.
|
|
71
73
|
|
|
72
74
|
Returns
|
|
73
75
|
-------
|
|
@@ -126,7 +128,8 @@ class JobConfig:
|
|
|
126
128
|
cls,
|
|
127
129
|
obj: Any,
|
|
128
130
|
) -> Self | None:
|
|
129
|
-
"""
|
|
131
|
+
"""
|
|
132
|
+
Parse a mapping into a :class:`JobConfig` instance.
|
|
130
133
|
|
|
131
134
|
Parameters
|
|
132
135
|
----------
|
|
@@ -194,12 +197,13 @@ class LoadRef:
|
|
|
194
197
|
cls,
|
|
195
198
|
obj: Any,
|
|
196
199
|
) -> Self | None:
|
|
197
|
-
"""
|
|
200
|
+
"""
|
|
201
|
+
Parse a mapping into a :class:`LoadRef` instance.
|
|
198
202
|
|
|
199
203
|
Parameters
|
|
200
204
|
----------
|
|
201
205
|
obj : Any
|
|
202
|
-
Mapping with
|
|
206
|
+
Mapping with :attr:`target` and optional :attr:`overrides`.
|
|
203
207
|
|
|
204
208
|
Returns
|
|
205
209
|
-------
|
|
@@ -240,12 +244,13 @@ class TransformRef:
|
|
|
240
244
|
cls,
|
|
241
245
|
obj: Any,
|
|
242
246
|
) -> Self | None:
|
|
243
|
-
"""
|
|
247
|
+
"""
|
|
248
|
+
Parse a mapping into a :class:`TransformRef` instance.
|
|
244
249
|
|
|
245
250
|
Parameters
|
|
246
251
|
----------
|
|
247
252
|
obj : Any
|
|
248
|
-
Mapping with
|
|
253
|
+
Mapping with :attr:`pipeline`.
|
|
249
254
|
|
|
250
255
|
Returns
|
|
251
256
|
-------
|
|
@@ -290,12 +295,13 @@ class ValidationRef:
|
|
|
290
295
|
cls,
|
|
291
296
|
obj: Any,
|
|
292
297
|
) -> Self | None:
|
|
293
|
-
"""
|
|
298
|
+
"""
|
|
299
|
+
Parse a mapping into a :class:`ValidationRef` instance.
|
|
294
300
|
|
|
295
301
|
Parameters
|
|
296
302
|
----------
|
|
297
303
|
obj : Any
|
|
298
|
-
Mapping with
|
|
304
|
+
Mapping with :attr:`ruleset` plus optional metadata.
|
|
299
305
|
|
|
300
306
|
Returns
|
|
301
307
|
-------
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
2
|
+
:mod:`etlplus.workflow.pipeline` module.
|
|
3
3
|
|
|
4
4
|
Pipeline configuration model and helpers for job orchestration.
|
|
5
5
|
|
|
@@ -38,7 +38,15 @@ from .utils import deep_substitute
|
|
|
38
38
|
# SECTION: EXPORTS ========================================================== #
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
__all__ = [
|
|
41
|
+
__all__ = [
|
|
42
|
+
# Data Classes
|
|
43
|
+
'PipelineConfig',
|
|
44
|
+
# Functions
|
|
45
|
+
'load_pipeline_config',
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# SECTION: INTERNAL FUNCTIONS =============================================== #
|
|
42
50
|
|
|
43
51
|
|
|
44
52
|
def _build_jobs(
|
|
@@ -156,7 +164,7 @@ def load_pipeline_config(
|
|
|
156
164
|
return PipelineConfig.from_yaml(path, substitute=substitute, env=env)
|
|
157
165
|
|
|
158
166
|
|
|
159
|
-
# SECTION: CLASSES
|
|
167
|
+
# SECTION: DATA CLASSES ===================================================== #
|
|
160
168
|
|
|
161
169
|
|
|
162
170
|
@dataclass(kw_only=True, slots=True)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
2
|
+
:mod:`etlplus.workflow.profile` module.
|
|
3
3
|
|
|
4
4
|
Profile model for pipeline-level defaults and environment.
|
|
5
5
|
|
|
@@ -22,10 +22,13 @@ from ..utils import cast_str_dict
|
|
|
22
22
|
# SECTION: EXPORTS ========================================================== #
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
__all__ = [
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Data Classes
|
|
27
|
+
'ProfileConfig',
|
|
28
|
+
]
|
|
26
29
|
|
|
27
30
|
|
|
28
|
-
# SECTION: CLASSES
|
|
31
|
+
# SECTION: DATA CLASSES ===================================================== #
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
@dataclass(kw_only=True, slots=True)
|
|
@@ -53,7 +56,7 @@ class ProfileConfig:
|
|
|
53
56
|
cls,
|
|
54
57
|
obj: StrAnyMap | None,
|
|
55
58
|
) -> Self:
|
|
56
|
-
"""Parse a mapping into a
|
|
59
|
+
"""Parse a mapping into a :class:`ProfileConfig` instance.
|
|
57
60
|
|
|
58
61
|
Parameters
|
|
59
62
|
----------
|
|
@@ -64,7 +67,7 @@ class ProfileConfig:
|
|
|
64
67
|
-------
|
|
65
68
|
Self
|
|
66
69
|
Parsed profile configuration; non-mapping input yields a default
|
|
67
|
-
instance. All
|
|
70
|
+
instance. All :attr:`env` values are coerced to strings.
|
|
68
71
|
"""
|
|
69
72
|
if not isinstance(obj, Mapping):
|
|
70
73
|
return cls()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
:mod:`etlplus.workflow.types` module.
|
|
3
|
+
|
|
4
|
+
Type aliases and editor-only :class:`TypedDict`s for :mod:`etlplus.config`.
|
|
5
|
+
|
|
6
|
+
These types improve IDE autocomplete and static analysis while the runtime
|
|
7
|
+
parsers remain permissive.
|
|
8
|
+
|
|
9
|
+
Notes
|
|
10
|
+
-----
|
|
11
|
+
- TypedDicts in this module are intentionally ``total=False`` and are not
|
|
12
|
+
enforced at runtime.
|
|
13
|
+
- :meth:`*.from_obj` constructors accept :class:`Mapping[str, Any]` and perform
|
|
14
|
+
tolerant parsing and light casting. This keeps the runtime permissive while
|
|
15
|
+
improving autocomplete and static analysis for contributors.
|
|
16
|
+
|
|
17
|
+
Examples
|
|
18
|
+
--------
|
|
19
|
+
>>> from etlplus.workflow import Connector
|
|
20
|
+
>>> src: Connector = {
|
|
21
|
+
>>> "type": "file",
|
|
22
|
+
>>> "path": "/data/input.csv",
|
|
23
|
+
>>> }
|
|
24
|
+
>>> tgt: Connector = {
|
|
25
|
+
>>> "type": "database",
|
|
26
|
+
>>> "connection_string": "postgresql://user:pass@localhost/db",
|
|
27
|
+
>>> }
|
|
28
|
+
>>> from etlplus.api import RetryPolicy
|
|
29
|
+
>>> rp: RetryPolicy = {"max_attempts": 3, "backoff": 0.5}
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from typing import Literal
|
|
35
|
+
from typing import TypedDict
|
|
36
|
+
|
|
37
|
+
from ..api import PaginationConfigMap
|
|
38
|
+
from ..api import RateLimitConfigMap
|
|
39
|
+
from ..types import StrAnyMap
|
|
40
|
+
|
|
41
|
+
# SECTION: EXPORTS ========================================================= #
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# Type Aliases
|
|
46
|
+
'ConnectorType',
|
|
47
|
+
# Typed Dicts
|
|
48
|
+
'ConnectorApiConfigMap',
|
|
49
|
+
'ConnectorDbConfigMap',
|
|
50
|
+
'ConnectorFileConfigMap',
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# SECTION: TYPE ALIASES ===================================================== #
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Literal type for supported connector kinds
|
|
58
|
+
type ConnectorType = Literal['api', 'database', 'file']
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# SECTION: TYPED DICTS ====================================================== #
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ConnectorApiConfigMap(TypedDict, total=False):
|
|
65
|
+
"""
|
|
66
|
+
Shape accepted by :meth:`ConnectorApi.from_obj` (all keys optional).
|
|
67
|
+
|
|
68
|
+
See Also
|
|
69
|
+
--------
|
|
70
|
+
- :meth:`etlplus.workflow.connector.ConnectorApi.from_obj`
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
name: str
|
|
74
|
+
type: ConnectorType
|
|
75
|
+
url: str
|
|
76
|
+
method: str
|
|
77
|
+
headers: StrAnyMap
|
|
78
|
+
query_params: StrAnyMap
|
|
79
|
+
pagination: PaginationConfigMap
|
|
80
|
+
rate_limit: RateLimitConfigMap
|
|
81
|
+
api: str
|
|
82
|
+
endpoint: str
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ConnectorDbConfigMap(TypedDict, total=False):
|
|
86
|
+
"""
|
|
87
|
+
Shape accepted by :meth:`ConnectorDb.from_obj` (all keys optional).
|
|
88
|
+
|
|
89
|
+
See Also
|
|
90
|
+
--------
|
|
91
|
+
- :meth:`etlplus.workflow.connector.ConnectorDb.from_obj`
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
name: str
|
|
95
|
+
type: ConnectorType
|
|
96
|
+
connection_string: str
|
|
97
|
+
query: str
|
|
98
|
+
table: str
|
|
99
|
+
mode: str
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ConnectorFileConfigMap(TypedDict, total=False):
|
|
103
|
+
"""
|
|
104
|
+
Shape accepted by :meth:`ConnectorFile.from_obj` (all keys optional).
|
|
105
|
+
|
|
106
|
+
See Also
|
|
107
|
+
--------
|
|
108
|
+
- :meth:`etlplus.workflow.connector.ConnectorFile.from_obj`
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
name: str
|
|
112
|
+
type: ConnectorType
|
|
113
|
+
format: str
|
|
114
|
+
path: str
|
|
115
|
+
options: StrAnyMap
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
etlplus/README.md,sha256=
|
|
1
|
+
etlplus/README.md,sha256=NwnsMajz2_afAXgYDnTIwVU6uBoOKQn8f6UJHUY-Xe8,1461
|
|
2
2
|
etlplus/__init__.py,sha256=mgTP4PJmRmsEjTCAizzzdtzAmhuHtarmPzphzdjvLgM,277
|
|
3
3
|
etlplus/__main__.py,sha256=btoROneNiigyfBU7BSzPKZ1R9gzBMpxcpsbPwmuHwTM,479
|
|
4
4
|
etlplus/__version__.py,sha256=1E0GMK_yUWCMQFKxXjTvyMwofi0qT2k4CDNiHWiymWE,327
|
|
@@ -8,7 +8,7 @@ etlplus/mixins.py,sha256=ifGpHwWv7U00yqGf-kN93vJax2IiK4jaGtTsPsO3Oak,1350
|
|
|
8
8
|
etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
etlplus/types.py,sha256=LhyKU67CW7CcLEshE_1OIvEqWr-QVMJVYByf1221miY,6161
|
|
10
10
|
etlplus/utils.py,sha256=BMLTWAvCJj3zLEcffBgURYnu0UGhhXsfH2WWpAt7fV8,13363
|
|
11
|
-
etlplus/api/README.md,sha256=
|
|
11
|
+
etlplus/api/README.md,sha256=Glxz9Y5RagqJomCPc8q3Nagm-23Owj9aAd8fM0H-Ph8,7917
|
|
12
12
|
etlplus/api/__init__.py,sha256=PK2lQv1FbsE7ZZS_ejevFZQSuOUHGApBc22YfHAzMqA,4615
|
|
13
13
|
etlplus/api/auth.py,sha256=GOO5on-LoMS1GXTAhtK9rFcfpjbBcNeA6NE5UZwIq0g,12158
|
|
14
14
|
etlplus/api/config.py,sha256=Xi1_FUaogQxAAx7LUTGwYr5vEB_SIFvjU_xgMGF2Fsc,17913
|
|
@@ -19,7 +19,7 @@ etlplus/api/request_manager.py,sha256=fhzPV5x7DqpKqoLvfDR8GKhBX_QBMtvZsRXxVnQQEl
|
|
|
19
19
|
etlplus/api/retry_manager.py,sha256=0GDhJVyIlb1Ww35JUWlYoa8QYUPjKLBtxQeZj3TdLbY,11306
|
|
20
20
|
etlplus/api/transport.py,sha256=DVWzWFZLfQQXaTJ60w-SzdXRxiOE8llPprpUB5CtSyg,9410
|
|
21
21
|
etlplus/api/types.py,sha256=ijHgREN5akEFF3th2yNdWbhJhXvAxh_24qQgg_sMB4w,4653
|
|
22
|
-
etlplus/api/utils.py,sha256=
|
|
22
|
+
etlplus/api/utils.py,sha256=U39OXcrPiLD9m0Y03pACXIIOyuuDPO6RRKuTcwEayBE,26249
|
|
23
23
|
etlplus/api/pagination/__init__.py,sha256=a4UX2J0AG8RMvmHt_CCofUm5vSmFo6GAfkb8XnSXypM,1395
|
|
24
24
|
etlplus/api/pagination/client.py,sha256=yMEpWqRxTCD4zRc9OYtEyUtShpGH5atiHFEAt95v2FE,5394
|
|
25
25
|
etlplus/api/pagination/config.py,sha256=3dXDJ-nMbO9Zk6i344n4roBFbUlHsa294D1_plPmm6E,13579
|
|
@@ -27,32 +27,27 @@ etlplus/api/pagination/paginator.py,sha256=wtdY_er4yfjx5yTUQJ1gPq-IuWmpLAHeG5buB
|
|
|
27
27
|
etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-Y_lbU6kI,1070
|
|
28
28
|
etlplus/api/rate_limiting/config.py,sha256=eB-dSOG-c5awYeEpnDsQJ5pO8BJtEpGYxOunBetWiB0,9775
|
|
29
29
|
etlplus/api/rate_limiting/rate_limiter.py,sha256=heAasm1KLeBFmoJKPz6W-eQbNuahMgG1E9-z8zGEUro,7035
|
|
30
|
-
etlplus/cli/README.md,sha256=
|
|
30
|
+
etlplus/cli/README.md,sha256=8H_G2d3HteYIU6ReX9K9DM485QjWDT5vHMQbGD_vv20,1237
|
|
31
31
|
etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
|
|
32
32
|
etlplus/cli/commands.py,sha256=g8_m3A8HEMyTRu2HctNiRoi2gtB5oSZCUEcyq-PIXos,24669
|
|
33
33
|
etlplus/cli/constants.py,sha256=E6Uy4WauLa_0zkzxqImXh-bb1gKdb9sBZQVc8QOzr2Q,1943
|
|
34
|
-
etlplus/cli/handlers.py,sha256=
|
|
34
|
+
etlplus/cli/handlers.py,sha256=mILI386pf2RzfI7WJfXJ13huIGwsqFfcYx4sNIf1aWU,18174
|
|
35
35
|
etlplus/cli/io.py,sha256=EFaBPYaBOyOllfMQWXgTjy-MPiGfNejicpD7ROrPyAE,7840
|
|
36
36
|
etlplus/cli/main.py,sha256=IgeqxypixfwLHR-QcpgVMQ7vMZ865bXOh2oO9v-BWeM,5234
|
|
37
37
|
etlplus/cli/options.py,sha256=vfXT3YLh7wG1iC-aTdSg6ItMC8l6n0Lozmy53XjqLbA,1199
|
|
38
38
|
etlplus/cli/state.py,sha256=Pfd8ru0wYIN7eGp1_A0tioqs1LiCDZCuJ6AnjZb6yYQ,8027
|
|
39
39
|
etlplus/cli/types.py,sha256=tclhKVJXDqHzlTQBYKARfqMgDOcuBJ-Zej2pvFy96WM,652
|
|
40
|
-
etlplus/config/README.md,sha256=
|
|
41
|
-
etlplus/config/__init__.py,sha256=
|
|
42
|
-
etlplus/config/
|
|
43
|
-
etlplus/
|
|
44
|
-
etlplus/config/pipeline.py,sha256=m4Jh0ctFcKrIx6zR7LEC0sYY5wq0o8NqOruWPlz6qmA,9494
|
|
45
|
-
etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,1898
|
|
46
|
-
etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
|
|
47
|
-
etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
|
|
48
|
-
etlplus/database/README.md,sha256=nUQVwLwGzmpA5_u4oYJYkPIhIuJAIdWsoeRgr4joAIE,1431
|
|
40
|
+
etlplus/config/README.md,sha256=CBeUW6IBfS8_U1iW4LzMQiAshcaTr2hgMDW3bos3BUY,1467
|
|
41
|
+
etlplus/config/__init__.py,sha256=rjUC-OUH_dXqG11B5r0hlJuss1J_nSinkvoygfuqId8,921
|
|
42
|
+
etlplus/config/types.py,sha256=3EyibjzqjK3GQu0fsvbmkfHERDtubEmtOU_lXsgXQuQ,3603
|
|
43
|
+
etlplus/database/README.md,sha256=3Af5BEGLkBmMoGOLtS1GQuj4wKPh_CwSp5NEPMf2uaY,1435
|
|
49
44
|
etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
|
|
50
45
|
etlplus/database/ddl.py,sha256=0dEM9SJMMabkhI_h-Fc0j9a1Sl5lSyZdI0bIeBVGm10,7913
|
|
51
46
|
etlplus/database/engine.py,sha256=PUxXLvLPyc-KaxuW7fXe12wYci7EvUp-Ad1H3bGhUog,4058
|
|
52
47
|
etlplus/database/orm.py,sha256=gCSqH-CjQz6tV9133-VqgiwokK5ylun0BwXaIWfImAo,10008
|
|
53
48
|
etlplus/database/schema.py,sha256=813C0Dd3WE53KTYot4dgjAxctgKXLXx-8_Rk_4r2e28,7022
|
|
54
49
|
etlplus/database/types.py,sha256=_pkQyC14TzAlgyeIqZG4F5LWYknZbHw3TW68Auk7Ya0,795
|
|
55
|
-
etlplus/file/README.md,sha256=
|
|
50
|
+
etlplus/file/README.md,sha256=ivU8svVs1fktQiW5ozvh1N-IOSLCAQ3oM9bW8DUFwIw,3630
|
|
56
51
|
etlplus/file/__init__.py,sha256=X03bosSM-uSd6dh3ur0un6_ozFRw2Tm4PE6kVUjtXK8,475
|
|
57
52
|
etlplus/file/_imports.py,sha256=9e8CWjyNIRcmiULEPuwtnJELUOXd4EvVv_vDnDYiB9c,3121
|
|
58
53
|
etlplus/file/_io.py,sha256=GXTcvjfXQX4rSdyu1JNhFmqQJlirDqd8dEGCN3dHvNg,2968
|
|
@@ -119,17 +114,26 @@ etlplus/ops/README.md,sha256=8omi7DYZhelc26JKk8Cm8QR8I3OGwziysPj1ivx41iQ,1380
|
|
|
119
114
|
etlplus/ops/__init__.py,sha256=NIIr2f-AZj5B0piBt6gjv46Yn0SzGYxEe6BPoopRh38,1702
|
|
120
115
|
etlplus/ops/extract.py,sha256=OJozX25qTjME7m9aTdVPJScT3GHHgopGM8uHo_rHm1Y,5783
|
|
121
116
|
etlplus/ops/load.py,sha256=RgEKnVygRN2cPDpzihU8UsIhd4eVoj0cPe0jBuNC0u4,8328
|
|
122
|
-
etlplus/ops/run.py,sha256=
|
|
117
|
+
etlplus/ops/run.py,sha256=JOPQxrciRUQ67FJhUcZ-pW1aiYGZUdxHLwhzWLNCaDo,13528
|
|
123
118
|
etlplus/ops/transform.py,sha256=1P43WYUaw872JDU86FhbbbkP8xnBWpgEPn2Q-z4ywls,25421
|
|
124
119
|
etlplus/ops/utils.py,sha256=tbV-s3NjIGO-GfdyJFxPYIYihPibNo-4TbdRq9vWVWs,11480
|
|
125
120
|
etlplus/ops/validate.py,sha256=gLa5zcwhxGbaIhH-OqTwDsQAgAYZSajLVGwwyeT2OZk,13257
|
|
126
|
-
etlplus/templates/README.md,sha256=
|
|
121
|
+
etlplus/templates/README.md,sha256=IfPXlj1TGVA-uFWosHJhE2rabFW-znxOlOMazO9Z5cE,1361
|
|
127
122
|
etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
|
|
128
123
|
etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
|
|
129
124
|
etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
|
|
130
|
-
etlplus
|
|
131
|
-
etlplus
|
|
132
|
-
etlplus
|
|
133
|
-
etlplus
|
|
134
|
-
etlplus
|
|
135
|
-
etlplus
|
|
125
|
+
etlplus/workflow/README.md,sha256=D1oloiJCOHiqpqgv3m3qpRSIUOMIQcWtIsOPv7KkNI0,1652
|
|
126
|
+
etlplus/workflow/__init__.py,sha256=LxI9VGlDBUc9ADoK8JNn3oVsGeaz1Uhjonn4Y5KIJdM,967
|
|
127
|
+
etlplus/workflow/connector.py,sha256=9b4KF2uNKaJtsQadwT7Id8OujfJM8GLJHqI-n4kFsGA,9851
|
|
128
|
+
etlplus/workflow/dag.py,sha256=kp31dORgk0GHbct_bipU5hu_0elwBtwLsXGjMWuhFHI,2503
|
|
129
|
+
etlplus/workflow/jobs.py,sha256=yFNj4S7ORMtpYSA0nCAUgb4E5msW7dyvtQVPBb-CAK8,7938
|
|
130
|
+
etlplus/workflow/pipeline.py,sha256=DL6XsX47gGPiURH8Qj1Ixm0RctI3taqo3e6Asg8fpto,9624
|
|
131
|
+
etlplus/workflow/profile.py,sha256=dZ6P50k_ZqXnrbgrbODUqgVkymbchcEqfZR-ExjTd3M,1935
|
|
132
|
+
etlplus/workflow/types.py,sha256=kVdOMT8qwnWlD7C4qWv-uF7r6-Kk-RlMZmoY8tnIo7A,2769
|
|
133
|
+
etlplus/workflow/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
|
|
134
|
+
etlplus-0.15.0.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
|
|
135
|
+
etlplus-0.15.0.dist-info/METADATA,sha256=YdRPU6L9SNAYpfB6fypG3-KewetI_QoyDlE2WnS2wPQ,28115
|
|
136
|
+
etlplus-0.15.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
137
|
+
etlplus-0.15.0.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
|
|
138
|
+
etlplus-0.15.0.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
|
|
139
|
+
etlplus-0.15.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|