etlplus 0.9.2__py3-none-any.whl → 0.10.2__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/__init__.py +26 -1
- etlplus/api/README.md +3 -51
- etlplus/api/__init__.py +0 -10
- etlplus/api/config.py +28 -39
- etlplus/api/endpoint_client.py +3 -3
- etlplus/api/pagination/client.py +1 -1
- etlplus/api/rate_limiting/config.py +1 -13
- etlplus/api/rate_limiting/rate_limiter.py +11 -8
- etlplus/api/request_manager.py +6 -11
- etlplus/api/transport.py +2 -14
- etlplus/api/types.py +6 -96
- etlplus/cli/commands.py +43 -76
- etlplus/cli/constants.py +1 -1
- etlplus/cli/handlers.py +12 -40
- etlplus/cli/io.py +2 -2
- etlplus/cli/main.py +1 -1
- etlplus/cli/state.py +7 -4
- etlplus/{workflow → config}/__init__.py +23 -10
- etlplus/{workflow → config}/connector.py +44 -58
- etlplus/{workflow → config}/jobs.py +32 -105
- etlplus/{workflow → config}/pipeline.py +51 -59
- etlplus/{workflow → config}/profile.py +5 -8
- etlplus/config/types.py +204 -0
- etlplus/config/utils.py +120 -0
- etlplus/database/ddl.py +1 -1
- etlplus/database/engine.py +3 -19
- etlplus/database/orm.py +0 -2
- etlplus/database/schema.py +1 -1
- etlplus/enums.py +288 -0
- etlplus/{ops/extract.py → extract.py} +99 -81
- etlplus/file.py +652 -0
- etlplus/{ops/load.py → load.py} +101 -78
- etlplus/{ops/run.py → run.py} +127 -159
- etlplus/{api/utils.py → run_helpers.py} +153 -209
- etlplus/{ops/transform.py → transform.py} +68 -75
- etlplus/types.py +4 -5
- etlplus/utils.py +2 -136
- etlplus/{ops/validate.py → validate.py} +12 -22
- etlplus/validation/__init__.py +44 -0
- etlplus/{ops → validation}/utils.py +17 -53
- {etlplus-0.9.2.dist-info → etlplus-0.10.2.dist-info}/METADATA +17 -210
- etlplus-0.10.2.dist-info/RECORD +65 -0
- {etlplus-0.9.2.dist-info → etlplus-0.10.2.dist-info}/WHEEL +1 -1
- etlplus/README.md +0 -37
- etlplus/api/enums.py +0 -51
- etlplus/cli/README.md +0 -40
- etlplus/database/README.md +0 -48
- etlplus/file/README.md +0 -105
- etlplus/file/__init__.py +0 -25
- etlplus/file/_imports.py +0 -141
- etlplus/file/_io.py +0 -160
- etlplus/file/accdb.py +0 -78
- etlplus/file/arrow.py +0 -78
- etlplus/file/avro.py +0 -176
- etlplus/file/bson.py +0 -77
- etlplus/file/cbor.py +0 -78
- etlplus/file/cfg.py +0 -79
- etlplus/file/conf.py +0 -80
- etlplus/file/core.py +0 -322
- etlplus/file/csv.py +0 -79
- etlplus/file/dat.py +0 -78
- etlplus/file/dta.py +0 -77
- etlplus/file/duckdb.py +0 -78
- etlplus/file/enums.py +0 -343
- etlplus/file/feather.py +0 -111
- etlplus/file/fwf.py +0 -77
- etlplus/file/gz.py +0 -123
- etlplus/file/hbs.py +0 -78
- etlplus/file/hdf5.py +0 -78
- etlplus/file/ini.py +0 -79
- etlplus/file/ion.py +0 -78
- etlplus/file/jinja2.py +0 -78
- etlplus/file/json.py +0 -98
- etlplus/file/log.py +0 -78
- etlplus/file/mat.py +0 -78
- etlplus/file/mdb.py +0 -78
- etlplus/file/msgpack.py +0 -78
- etlplus/file/mustache.py +0 -78
- etlplus/file/nc.py +0 -78
- etlplus/file/ndjson.py +0 -108
- etlplus/file/numbers.py +0 -75
- etlplus/file/ods.py +0 -79
- etlplus/file/orc.py +0 -111
- etlplus/file/parquet.py +0 -113
- etlplus/file/pb.py +0 -78
- etlplus/file/pbf.py +0 -77
- etlplus/file/properties.py +0 -78
- etlplus/file/proto.py +0 -77
- etlplus/file/psv.py +0 -79
- etlplus/file/rda.py +0 -78
- etlplus/file/rds.py +0 -78
- etlplus/file/sas7bdat.py +0 -78
- etlplus/file/sav.py +0 -77
- etlplus/file/sqlite.py +0 -78
- etlplus/file/stub.py +0 -84
- etlplus/file/sylk.py +0 -77
- etlplus/file/tab.py +0 -81
- etlplus/file/toml.py +0 -78
- etlplus/file/tsv.py +0 -80
- etlplus/file/txt.py +0 -102
- etlplus/file/vm.py +0 -78
- etlplus/file/wks.py +0 -77
- etlplus/file/xls.py +0 -88
- etlplus/file/xlsm.py +0 -79
- etlplus/file/xlsx.py +0 -99
- etlplus/file/xml.py +0 -185
- etlplus/file/xpt.py +0 -78
- etlplus/file/yaml.py +0 -95
- etlplus/file/zip.py +0 -175
- etlplus/file/zsav.py +0 -77
- etlplus/ops/README.md +0 -50
- etlplus/ops/__init__.py +0 -61
- etlplus/templates/README.md +0 -46
- etlplus/workflow/README.md +0 -52
- etlplus/workflow/dag.py +0 -105
- etlplus/workflow/types.py +0 -115
- etlplus-0.9.2.dist-info/RECORD +0 -134
- {etlplus-0.9.2.dist-info → etlplus-0.10.2.dist-info}/entry_points.txt +0 -0
- {etlplus-0.9.2.dist-info → etlplus-0.10.2.dist-info}/licenses/LICENSE +0 -0
- {etlplus-0.9.2.dist-info → etlplus-0.10.2.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
"""
|
|
2
|
-
:mod:`etlplus.
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
:mod:`etlplus.run_helpers` module.
|
|
3
|
+
|
|
4
|
+
Helper functions and small utilities used by ``etlplus.run`` to compose API
|
|
5
|
+
request/load environments, pagination configs, session objects, and endpoint
|
|
6
|
+
clients. Extracted to keep ``run.py`` focused on orchestration while enabling
|
|
7
|
+
reuse and testability.
|
|
8
|
+
|
|
9
|
+
Public (re-export safe) helpers:
|
|
10
|
+
- build_pagination_cfg(pagination, overrides)
|
|
11
|
+
- build_session(cfg)
|
|
12
|
+
- compose_api_request_env(cfg, source_obj, extract_opts)
|
|
13
|
+
- compose_api_target_env(cfg, target_obj, overrides)
|
|
14
|
+
- build_endpoint_client(base_url, base_path, endpoints, env)
|
|
15
|
+
- compute_rl_sleep_seconds(rate_limit, overrides)
|
|
16
|
+
- paginate_with_client(client, endpoint_key, params, headers,
|
|
17
|
+
timeout, pagination, sleep_seconds)
|
|
18
|
+
|
|
19
|
+
Notes
|
|
20
|
+
-----
|
|
21
|
+
These helpers intentionally accept permissive ``Any``/``Mapping`` inputs to
|
|
22
|
+
avoid tight coupling with config dataclasses while keeping runtime flexible.
|
|
5
23
|
"""
|
|
6
24
|
|
|
7
25
|
from __future__ import annotations
|
|
8
26
|
|
|
9
27
|
import inspect
|
|
10
|
-
from collections.abc import Callable
|
|
11
28
|
from collections.abc import Mapping
|
|
12
29
|
from typing import Any
|
|
13
30
|
from typing import TypedDict
|
|
@@ -15,34 +32,24 @@ from typing import cast
|
|
|
15
32
|
|
|
16
33
|
import requests # type: ignore[import]
|
|
17
34
|
|
|
18
|
-
from
|
|
19
|
-
from
|
|
20
|
-
from .
|
|
21
|
-
from .
|
|
22
|
-
from .
|
|
23
|
-
from .
|
|
24
|
-
from .
|
|
25
|
-
from .
|
|
26
|
-
from .
|
|
27
|
-
from .
|
|
28
|
-
from .
|
|
29
|
-
from .
|
|
30
|
-
from .types import
|
|
31
|
-
from .types import Params
|
|
32
|
-
from .types import Url
|
|
33
|
-
|
|
34
|
-
# SECTION: CONSTANTS ======================================================== #
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
DEFAULT_TIMEOUT: float = 10.0
|
|
38
|
-
|
|
35
|
+
from .api import ApiConfig
|
|
36
|
+
from .api import EndpointClient
|
|
37
|
+
from .api import EndpointConfig
|
|
38
|
+
from .api import Headers
|
|
39
|
+
from .api import PaginationConfig
|
|
40
|
+
from .api import PaginationConfigMap
|
|
41
|
+
from .api import Params
|
|
42
|
+
from .api import RateLimitConfig
|
|
43
|
+
from .api import RateLimitConfigMap
|
|
44
|
+
from .api import RateLimiter
|
|
45
|
+
from .api import RetryPolicy
|
|
46
|
+
from .api import Url
|
|
47
|
+
from .types import Timeout
|
|
39
48
|
|
|
40
49
|
# SECTION: EXPORTS ========================================================== #
|
|
41
50
|
|
|
42
51
|
|
|
43
52
|
__all__ = [
|
|
44
|
-
# Constants
|
|
45
|
-
'DEFAULT_TIMEOUT',
|
|
46
53
|
# Functions
|
|
47
54
|
'build_endpoint_client',
|
|
48
55
|
'build_pagination_cfg',
|
|
@@ -51,7 +58,6 @@ __all__ = [
|
|
|
51
58
|
'compose_api_target_env',
|
|
52
59
|
'compute_rl_sleep_seconds',
|
|
53
60
|
'paginate_with_client',
|
|
54
|
-
'resolve_request',
|
|
55
61
|
# Typed Dicts
|
|
56
62
|
'ApiRequestEnv',
|
|
57
63
|
'ApiTargetEnv',
|
|
@@ -62,83 +68,43 @@ __all__ = [
|
|
|
62
68
|
# SECTION: TYPED DICTS ====================================================== #
|
|
63
69
|
|
|
64
70
|
|
|
65
|
-
class
|
|
66
|
-
"""
|
|
67
|
-
Common HTTP request environment for API interactions.
|
|
68
|
-
|
|
69
|
-
Fields shared by both source-side and target-side API operations.
|
|
70
|
-
"""
|
|
71
|
+
class ApiRequestEnv(TypedDict, total=False):
|
|
72
|
+
"""API request environment configuration."""
|
|
71
73
|
|
|
72
|
-
# Request details
|
|
73
74
|
url: Url | None
|
|
74
75
|
headers: dict[str, str]
|
|
75
76
|
timeout: Timeout
|
|
76
|
-
|
|
77
|
-
# Session
|
|
78
77
|
session: requests.Session | None
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class ApiRequestEnv(BaseApiHttpEnv, total=False):
|
|
82
|
-
"""
|
|
83
|
-
Composed HTTP request environment configuration for REST API sources.
|
|
84
|
-
|
|
85
|
-
Returned by :func:`compose_api_request_env` and consumed by the API extract
|
|
86
|
-
branch. Values are fully merged with endpoint/API defaults and job-level
|
|
87
|
-
overrides, preserving the original precedence and behavior.
|
|
88
|
-
"""
|
|
89
|
-
|
|
90
|
-
# Client
|
|
91
78
|
use_endpoints: bool
|
|
92
79
|
base_url: str | None
|
|
93
80
|
base_path: str | None
|
|
94
81
|
endpoints_map: dict[str, str] | None
|
|
95
82
|
endpoint_key: str | None
|
|
96
|
-
|
|
97
|
-
# Request
|
|
98
83
|
params: dict[str, Any]
|
|
99
84
|
pagination: PaginationConfigMap | None
|
|
100
85
|
sleep_seconds: float
|
|
101
|
-
|
|
102
|
-
# Reliability
|
|
103
86
|
retry: RetryPolicy | None
|
|
104
87
|
retry_network_errors: bool
|
|
105
88
|
|
|
106
89
|
|
|
107
|
-
class ApiTargetEnv(
|
|
108
|
-
"""
|
|
109
|
-
Composed HTTP request environment configuration for REST API targets.
|
|
110
|
-
|
|
111
|
-
Returned by :func:`compose_api_target_env` and consumed by the API load
|
|
112
|
-
branch. Values are merged from the target object, optional API/endpoint
|
|
113
|
-
reference, and job-level overrides, preserving original precedence and
|
|
114
|
-
behavior.
|
|
115
|
-
|
|
116
|
-
Notes
|
|
117
|
-
-----
|
|
118
|
-
- Precedence for inherited values matches original logic:
|
|
119
|
-
overrides -> target -> API profile defaults.
|
|
120
|
-
- Target composition does not include pagination/rate-limit/retry since
|
|
121
|
-
loads are single-request operations; only headers/timeout/session
|
|
122
|
-
apply.
|
|
123
|
-
"""
|
|
90
|
+
class ApiTargetEnv(TypedDict, total=False):
|
|
91
|
+
"""API target environment configuration."""
|
|
124
92
|
|
|
125
|
-
|
|
93
|
+
url: Url | None
|
|
94
|
+
headers: dict[str, str]
|
|
95
|
+
timeout: Timeout
|
|
96
|
+
session: requests.Session | None
|
|
126
97
|
method: str | None
|
|
127
98
|
|
|
128
99
|
|
|
129
100
|
class SessionConfig(TypedDict, total=False):
|
|
130
|
-
"""
|
|
131
|
-
Minimal session configuration schema accepted by the
|
|
132
|
-
:class:`requests.Session` runner.
|
|
133
|
-
|
|
134
|
-
Keys mirror common :class:`requests.Session` options; all are optional.
|
|
135
|
-
"""
|
|
101
|
+
"""Configuration for requests.Session."""
|
|
136
102
|
|
|
137
103
|
headers: Mapping[str, Any]
|
|
138
104
|
params: Mapping[str, Any]
|
|
139
|
-
auth: Any
|
|
105
|
+
auth: Any
|
|
140
106
|
verify: bool | str
|
|
141
|
-
cert: Any
|
|
107
|
+
cert: Any
|
|
142
108
|
proxies: Mapping[str, Any]
|
|
143
109
|
cookies: Mapping[str, Any]
|
|
144
110
|
trust_env: bool
|
|
@@ -147,47 +113,7 @@ class SessionConfig(TypedDict, total=False):
|
|
|
147
113
|
# SECTION: INTERNAL FUNCTIONS ============================================== #
|
|
148
114
|
|
|
149
115
|
|
|
150
|
-
|
|
151
|
-
cfg: SessionConfig | None,
|
|
152
|
-
) -> requests.Session | None:
|
|
153
|
-
"""
|
|
154
|
-
Return a configured session when *cfg* is a mapping.
|
|
155
|
-
|
|
156
|
-
Parameters
|
|
157
|
-
----------
|
|
158
|
-
cfg : SessionConfig | None
|
|
159
|
-
Session configuration mapping.
|
|
160
|
-
|
|
161
|
-
Returns
|
|
162
|
-
-------
|
|
163
|
-
requests.Session | None
|
|
164
|
-
Configured session or ``None``.
|
|
165
|
-
"""
|
|
166
|
-
if isinstance(cfg, Mapping):
|
|
167
|
-
return build_session(cast(SessionConfig, cfg))
|
|
168
|
-
return None
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def _coalesce(
|
|
172
|
-
*args: Any,
|
|
173
|
-
) -> Any | None:
|
|
174
|
-
"""
|
|
175
|
-
Return the first non-``None`` value from ``args``.
|
|
176
|
-
|
|
177
|
-
Parameters
|
|
178
|
-
----------
|
|
179
|
-
*args : Any
|
|
180
|
-
Candidate values in descending precedence order.
|
|
181
|
-
|
|
182
|
-
Returns
|
|
183
|
-
-------
|
|
184
|
-
Any | None
|
|
185
|
-
The first non-``None`` value, or ``None`` if all are ``None``.
|
|
186
|
-
"""
|
|
187
|
-
for arg in args:
|
|
188
|
-
if arg is not None:
|
|
189
|
-
return arg
|
|
190
|
-
return None
|
|
116
|
+
# -- API Environment Composition -- #
|
|
191
117
|
|
|
192
118
|
|
|
193
119
|
def _get_api_cfg_and_endpoint(
|
|
@@ -291,15 +217,37 @@ def _merge_session_cfg_three(
|
|
|
291
217
|
api_sess = getattr(api_cfg, 'session', None)
|
|
292
218
|
ep_sess = getattr(ep, 'session', None)
|
|
293
219
|
merged: dict[str, Any] = {}
|
|
294
|
-
if isinstance(api_sess,
|
|
220
|
+
if isinstance(api_sess, dict):
|
|
295
221
|
merged.update(api_sess)
|
|
296
|
-
if isinstance(ep_sess,
|
|
222
|
+
if isinstance(ep_sess, dict):
|
|
297
223
|
merged.update(ep_sess)
|
|
298
|
-
if isinstance(source_session_cfg,
|
|
224
|
+
if isinstance(source_session_cfg, dict):
|
|
299
225
|
merged.update(source_session_cfg)
|
|
300
226
|
return cast(SessionConfig | None, (merged or None))
|
|
301
227
|
|
|
302
228
|
|
|
229
|
+
# -- Mapping Helpers -- #
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _copy_mapping(
|
|
233
|
+
mapping: Mapping[str, Any] | None,
|
|
234
|
+
) -> dict[str, Any]:
|
|
235
|
+
"""
|
|
236
|
+
Return a shallow copy of *mapping* or an empty dict.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
mapping : Mapping[str, Any] | None
|
|
241
|
+
The mapping to copy.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
dict[str, Any]
|
|
246
|
+
A shallow copy of the mapping or an empty dict.
|
|
247
|
+
"""
|
|
248
|
+
return dict(mapping) if isinstance(mapping, Mapping) else {}
|
|
249
|
+
|
|
250
|
+
|
|
303
251
|
def _update_mapping(
|
|
304
252
|
target: dict[str, Any],
|
|
305
253
|
extra: Mapping[str, Any] | None,
|
|
@@ -318,9 +266,37 @@ def _update_mapping(
|
|
|
318
266
|
target.update(extra)
|
|
319
267
|
|
|
320
268
|
|
|
269
|
+
# -- Session -- #
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _build_session_optional(
|
|
273
|
+
cfg: SessionConfig | None,
|
|
274
|
+
) -> requests.Session | None:
|
|
275
|
+
"""
|
|
276
|
+
Return a configured session when *cfg* is a mapping.
|
|
277
|
+
|
|
278
|
+
Parameters
|
|
279
|
+
----------
|
|
280
|
+
cfg : SessionConfig | None
|
|
281
|
+
Session configuration mapping.
|
|
282
|
+
|
|
283
|
+
Returns
|
|
284
|
+
-------
|
|
285
|
+
requests.Session | None
|
|
286
|
+
Configured session or ``None``.
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
if isinstance(cfg, dict):
|
|
290
|
+
return build_session(cfg)
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
|
|
321
294
|
# SECTION: FUNCTIONS ======================================================== #
|
|
322
295
|
|
|
323
296
|
|
|
297
|
+
# -- API Environment Composition -- #
|
|
298
|
+
|
|
299
|
+
|
|
324
300
|
def build_endpoint_client(
|
|
325
301
|
*,
|
|
326
302
|
base_url: str,
|
|
@@ -347,7 +323,15 @@ def build_endpoint_client(
|
|
|
347
323
|
EndpointClient
|
|
348
324
|
The constructed endpoint client.
|
|
349
325
|
"""
|
|
350
|
-
|
|
326
|
+
# Allow tests to monkeypatch etlplus.run.EndpointClient and have it
|
|
327
|
+
# propagate here by preferring the class on the run module if present.
|
|
328
|
+
try:
|
|
329
|
+
from . import run as run_mod # local import to avoid cycles
|
|
330
|
+
|
|
331
|
+
ClientClass = getattr(run_mod, 'EndpointClient', EndpointClient)
|
|
332
|
+
except (ImportError, AttributeError): # pragma: no cover - fallback path
|
|
333
|
+
ClientClass = EndpointClient
|
|
334
|
+
return ClientClass(
|
|
351
335
|
base_url=base_url,
|
|
352
336
|
base_path=base_path,
|
|
353
337
|
endpoints=endpoints,
|
|
@@ -385,19 +369,21 @@ def compose_api_request_env(
|
|
|
385
369
|
Mapping[str, Any] | None,
|
|
386
370
|
getattr(source_obj, 'query_params', None),
|
|
387
371
|
)
|
|
388
|
-
params: dict[str, Any] =
|
|
372
|
+
params: dict[str, Any] = _copy_mapping(source_params)
|
|
389
373
|
source_headers = cast(
|
|
390
374
|
Mapping[str, str] | None,
|
|
391
375
|
getattr(source_obj, 'headers', None),
|
|
392
376
|
)
|
|
393
|
-
headers: dict[str, str] =
|
|
377
|
+
headers: dict[str, str] = _copy_mapping(source_headers)
|
|
394
378
|
pagination = getattr(source_obj, 'pagination', None)
|
|
395
379
|
rate_limit = getattr(source_obj, 'rate_limit', None)
|
|
396
380
|
retry: RetryPolicy | None = cast(
|
|
397
381
|
RetryPolicy | None,
|
|
398
382
|
getattr(source_obj, 'retry', None),
|
|
399
383
|
)
|
|
400
|
-
retry_network_errors =
|
|
384
|
+
retry_network_errors = bool(
|
|
385
|
+
getattr(source_obj, 'retry_network_errors', False),
|
|
386
|
+
)
|
|
401
387
|
session_cfg = cast(
|
|
402
388
|
SessionConfig | None,
|
|
403
389
|
getattr(source_obj, 'session', None),
|
|
@@ -419,33 +405,33 @@ def compose_api_request_env(
|
|
|
419
405
|
session_cfg,
|
|
420
406
|
force_url=True,
|
|
421
407
|
)
|
|
422
|
-
ep_params: dict[str, Any] =
|
|
408
|
+
ep_params: dict[str, Any] = _copy_mapping(
|
|
423
409
|
cast(Mapping[str, Any] | None, getattr(ep, 'query_params', None)),
|
|
424
410
|
)
|
|
425
411
|
_update_mapping(ep_params, params)
|
|
426
412
|
params = ep_params
|
|
427
|
-
pagination =
|
|
428
|
-
pagination
|
|
429
|
-
ep.pagination
|
|
430
|
-
api_cfg.effective_pagination_defaults()
|
|
413
|
+
pagination = (
|
|
414
|
+
pagination
|
|
415
|
+
or ep.pagination
|
|
416
|
+
or api_cfg.effective_pagination_defaults()
|
|
431
417
|
)
|
|
432
|
-
rate_limit =
|
|
433
|
-
rate_limit
|
|
434
|
-
ep.rate_limit
|
|
435
|
-
api_cfg.effective_rate_limit_defaults()
|
|
418
|
+
rate_limit = (
|
|
419
|
+
rate_limit
|
|
420
|
+
or ep.rate_limit
|
|
421
|
+
or api_cfg.effective_rate_limit_defaults()
|
|
436
422
|
)
|
|
437
423
|
retry = cast(
|
|
438
424
|
RetryPolicy | None,
|
|
439
|
-
|
|
440
|
-
retry
|
|
441
|
-
getattr(ep, 'retry', None)
|
|
442
|
-
getattr(api_cfg, 'retry', None)
|
|
425
|
+
(
|
|
426
|
+
retry
|
|
427
|
+
or getattr(ep, 'retry', None)
|
|
428
|
+
or getattr(api_cfg, 'retry', None)
|
|
443
429
|
),
|
|
444
430
|
)
|
|
445
|
-
retry_network_errors =
|
|
446
|
-
retry_network_errors
|
|
447
|
-
getattr(ep, 'retry_network_errors',
|
|
448
|
-
getattr(api_cfg, 'retry_network_errors',
|
|
431
|
+
retry_network_errors = (
|
|
432
|
+
retry_network_errors
|
|
433
|
+
or bool(getattr(ep, 'retry_network_errors', False))
|
|
434
|
+
or bool(getattr(api_cfg, 'retry_network_errors', False))
|
|
449
435
|
)
|
|
450
436
|
use_client_endpoints = True
|
|
451
437
|
client_base_url = api_cfg.base_url
|
|
@@ -480,10 +466,8 @@ def compose_api_request_env(
|
|
|
480
466
|
retry = rty_ov
|
|
481
467
|
if rne_ov is not None:
|
|
482
468
|
retry_network_errors = bool(rne_ov)
|
|
483
|
-
if isinstance(sess_ov,
|
|
484
|
-
base_cfg: dict[str, Any] = dict(
|
|
485
|
-
cast(Mapping[str, Any], session_cfg or {}),
|
|
486
|
-
)
|
|
469
|
+
if isinstance(sess_ov, dict):
|
|
470
|
+
base_cfg: dict[str, Any] = dict(cast(dict, session_cfg or {}))
|
|
487
471
|
base_cfg.update(sess_ov)
|
|
488
472
|
session_cfg = cast(SessionConfig, base_cfg)
|
|
489
473
|
pag_cfg: PaginationConfigMap | None = build_pagination_cfg(
|
|
@@ -504,7 +488,7 @@ def compose_api_request_env(
|
|
|
504
488
|
'pagination': pag_cfg,
|
|
505
489
|
'sleep_seconds': sleep_s,
|
|
506
490
|
'retry': retry,
|
|
507
|
-
'retry_network_errors':
|
|
491
|
+
'retry_network_errors': retry_network_errors,
|
|
508
492
|
'session': sess_obj,
|
|
509
493
|
}
|
|
510
494
|
|
|
@@ -540,14 +524,8 @@ def compose_api_target_env(
|
|
|
540
524
|
str | None,
|
|
541
525
|
ov.get('method') or getattr(target_obj, 'method', 'post'),
|
|
542
526
|
)
|
|
543
|
-
headers =
|
|
544
|
-
|
|
545
|
-
coerce_dict(
|
|
546
|
-
cast(
|
|
547
|
-
Mapping[str, str] | None,
|
|
548
|
-
getattr(target_obj, 'headers', None),
|
|
549
|
-
),
|
|
550
|
-
),
|
|
527
|
+
headers = _copy_mapping(
|
|
528
|
+
cast(Mapping[str, str] | None, getattr(target_obj, 'headers', None)),
|
|
551
529
|
)
|
|
552
530
|
_update_mapping(headers, cast(Mapping[str, str] | None, ov.get('headers')))
|
|
553
531
|
timeout: Timeout = (
|
|
@@ -580,6 +558,9 @@ def compose_api_target_env(
|
|
|
580
558
|
}
|
|
581
559
|
|
|
582
560
|
|
|
561
|
+
# -- Pagination -- #
|
|
562
|
+
|
|
563
|
+
|
|
583
564
|
def build_pagination_cfg(
|
|
584
565
|
pagination: PaginationConfig | None,
|
|
585
566
|
overrides: Mapping[str, Any] | None,
|
|
@@ -686,6 +667,9 @@ def build_pagination_cfg(
|
|
|
686
667
|
return cast(PaginationConfigMap, cfg)
|
|
687
668
|
|
|
688
669
|
|
|
670
|
+
# -- Pagination Invocation -- #
|
|
671
|
+
|
|
672
|
+
|
|
689
673
|
def paginate_with_client(
|
|
690
674
|
client: Any,
|
|
691
675
|
endpoint_key: str,
|
|
@@ -743,6 +727,9 @@ def paginate_with_client(
|
|
|
743
727
|
return client.paginate(endpoint_key, **kw_pag)
|
|
744
728
|
|
|
745
729
|
|
|
730
|
+
# -- Rate Limit -- #
|
|
731
|
+
|
|
732
|
+
|
|
746
733
|
def compute_rl_sleep_seconds(
|
|
747
734
|
rate_limit: RateLimitConfig | Mapping[str, Any] | None,
|
|
748
735
|
overrides: Mapping[str, Any] | None,
|
|
@@ -795,6 +782,9 @@ def compute_rl_sleep_seconds(
|
|
|
795
782
|
)
|
|
796
783
|
|
|
797
784
|
|
|
785
|
+
# -- Session -- #
|
|
786
|
+
|
|
787
|
+
|
|
798
788
|
def build_session(
|
|
799
789
|
cfg: SessionConfig | None,
|
|
800
790
|
) -> requests.Session:
|
|
@@ -815,12 +805,12 @@ def build_session(
|
|
|
815
805
|
if not cfg:
|
|
816
806
|
return s
|
|
817
807
|
headers = cfg.get('headers')
|
|
818
|
-
if isinstance(headers,
|
|
808
|
+
if isinstance(headers, dict):
|
|
819
809
|
s.headers.update(headers)
|
|
820
810
|
params = cfg.get('params')
|
|
821
|
-
if isinstance(params,
|
|
811
|
+
if isinstance(params, dict):
|
|
822
812
|
try:
|
|
823
|
-
s.params =
|
|
813
|
+
s.params = params
|
|
824
814
|
except (AttributeError, TypeError):
|
|
825
815
|
pass
|
|
826
816
|
auth = cfg.get('auth')
|
|
@@ -835,12 +825,12 @@ def build_session(
|
|
|
835
825
|
if cert is not None:
|
|
836
826
|
s.cert = cert # type: ignore[assignment]
|
|
837
827
|
proxies = cfg.get('proxies')
|
|
838
|
-
if isinstance(proxies,
|
|
828
|
+
if isinstance(proxies, dict):
|
|
839
829
|
s.proxies.update(proxies)
|
|
840
830
|
cookies = cfg.get('cookies')
|
|
841
|
-
if isinstance(cookies,
|
|
831
|
+
if isinstance(cookies, dict):
|
|
842
832
|
try:
|
|
843
|
-
s.cookies.update(
|
|
833
|
+
s.cookies.update(cookies)
|
|
844
834
|
except (TypeError, ValueError):
|
|
845
835
|
pass
|
|
846
836
|
if 'trust_env' in cfg:
|
|
@@ -851,49 +841,3 @@ def build_session(
|
|
|
851
841
|
pass
|
|
852
842
|
|
|
853
843
|
return s
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
def resolve_request(
|
|
857
|
-
method: HttpMethod | str,
|
|
858
|
-
*,
|
|
859
|
-
session: Any | None = None,
|
|
860
|
-
timeout: Timeout = None,
|
|
861
|
-
) -> tuple[Callable[..., requests.Response], float, HttpMethod]:
|
|
862
|
-
"""
|
|
863
|
-
Resolve a request callable and effective timeout for an HTTP method.
|
|
864
|
-
|
|
865
|
-
Parameters
|
|
866
|
-
----------
|
|
867
|
-
method : HttpMethod | str
|
|
868
|
-
HTTP method to execute.
|
|
869
|
-
session : Any | None, optional
|
|
870
|
-
Requests-compatible session object. Defaults to module-level
|
|
871
|
-
``requests``.
|
|
872
|
-
timeout : Timeout, optional
|
|
873
|
-
Timeout in seconds for the request. Uses ``DEFAULT_TIMEOUT`` when
|
|
874
|
-
omitted.
|
|
875
|
-
|
|
876
|
-
Returns
|
|
877
|
-
-------
|
|
878
|
-
tuple[Callable[..., requests.Response], float, HttpMethod]
|
|
879
|
-
Tuple of (callable, timeout_seconds, resolved_method).
|
|
880
|
-
|
|
881
|
-
Raises
|
|
882
|
-
------
|
|
883
|
-
TypeError
|
|
884
|
-
If the session object does not expose the requested HTTP method.
|
|
885
|
-
"""
|
|
886
|
-
http_method = HttpMethod.coerce(method)
|
|
887
|
-
request_timeout = DEFAULT_TIMEOUT if timeout is None else timeout
|
|
888
|
-
requester = session or requests
|
|
889
|
-
request_callable = getattr(requester, http_method.value, None)
|
|
890
|
-
if not callable(request_callable):
|
|
891
|
-
raise TypeError(
|
|
892
|
-
'Session object must supply a callable '
|
|
893
|
-
f'"{http_method.value}" method',
|
|
894
|
-
)
|
|
895
|
-
typed_request_callable = cast(
|
|
896
|
-
Callable[..., requests.Response],
|
|
897
|
-
request_callable,
|
|
898
|
-
)
|
|
899
|
-
return typed_request_callable, request_timeout, http_method
|