ddeutil-workflow 0.0.4__py3-none-any.whl → 0.0.6__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.
- ddeutil/workflow/__about__.py +1 -1
- ddeutil/workflow/__init__.py +9 -0
- ddeutil/workflow/__types.py +43 -1
- ddeutil/workflow/exceptions.py +13 -1
- ddeutil/workflow/loader.py +16 -110
- ddeutil/workflow/on.py +195 -0
- ddeutil/workflow/pipeline.py +351 -371
- ddeutil/workflow/{vendors/__schedule.py → scheduler.py} +222 -176
- ddeutil/workflow/stage.py +402 -0
- ddeutil/workflow/utils.py +219 -28
- {ddeutil_workflow-0.0.4.dist-info → ddeutil_workflow-0.0.6.dist-info}/METADATA +118 -90
- ddeutil_workflow-0.0.6.dist-info/RECORD +15 -0
- {ddeutil_workflow-0.0.4.dist-info → ddeutil_workflow-0.0.6.dist-info}/WHEEL +1 -1
- ddeutil/workflow/__regex.py +0 -44
- ddeutil/workflow/conn.py +0 -240
- ddeutil/workflow/schedule.py +0 -82
- ddeutil/workflow/tasks/__init__.py +0 -6
- ddeutil/workflow/tasks/_pandas.py +0 -54
- ddeutil/workflow/tasks/_polars.py +0 -92
- ddeutil/workflow/vendors/__dataset.py +0 -127
- ddeutil/workflow/vendors/__dict.py +0 -333
- ddeutil/workflow/vendors/__init__.py +0 -0
- ddeutil/workflow/vendors/aws.py +0 -185
- ddeutil/workflow/vendors/az.py +0 -0
- ddeutil/workflow/vendors/minio.py +0 -11
- ddeutil/workflow/vendors/pd.py +0 -13
- ddeutil/workflow/vendors/pg.py +0 -11
- ddeutil/workflow/vendors/pl.py +0 -172
- ddeutil/workflow/vendors/sftp.py +0 -209
- ddeutil_workflow-0.0.4.dist-info/RECORD +0 -29
- {ddeutil_workflow-0.0.4.dist-info → ddeutil_workflow-0.0.6.dist-info}/LICENSE +0 -0
- {ddeutil_workflow-0.0.4.dist-info → ddeutil_workflow-0.0.6.dist-info}/top_level.txt +0 -0
@@ -1,333 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Reference:
|
3
|
-
* https://github.com/LarsHill/metadict
|
4
|
-
"""
|
5
|
-
|
6
|
-
from __future__ import annotations
|
7
|
-
|
8
|
-
import contextlib
|
9
|
-
import copy
|
10
|
-
import keyword
|
11
|
-
import re
|
12
|
-
import warnings
|
13
|
-
from collections.abc import (
|
14
|
-
Iterable,
|
15
|
-
Iterator,
|
16
|
-
KeysView,
|
17
|
-
Mapping,
|
18
|
-
MutableMapping,
|
19
|
-
)
|
20
|
-
from re import Pattern
|
21
|
-
from typing import (
|
22
|
-
Any,
|
23
|
-
Optional,
|
24
|
-
TypeVar,
|
25
|
-
)
|
26
|
-
|
27
|
-
from typing_extensions import Self
|
28
|
-
|
29
|
-
|
30
|
-
def _warning(
|
31
|
-
message,
|
32
|
-
category=UserWarning,
|
33
|
-
filename="",
|
34
|
-
lineno=-1,
|
35
|
-
file=None,
|
36
|
-
line="",
|
37
|
-
):
|
38
|
-
"""Monkey patch `warnings` to show UserWarning without the line information
|
39
|
-
of warnings call.
|
40
|
-
"""
|
41
|
-
msg = warnings.WarningMessage(
|
42
|
-
message, category, filename, lineno, file, line
|
43
|
-
)
|
44
|
-
print(f"{msg.category.__name__}: {msg.message}")
|
45
|
-
|
46
|
-
|
47
|
-
warnings.showwarning = _warning
|
48
|
-
|
49
|
-
KT = TypeVar("KT")
|
50
|
-
VT = TypeVar("VT")
|
51
|
-
|
52
|
-
# NOTE: regex to enforce python variable/attribute syntax
|
53
|
-
ALLOWED_VAR_SYNTAX: Pattern = re.compile(r"[a-zA-Z_]\w*")
|
54
|
-
|
55
|
-
|
56
|
-
def complies_variable_syntax(name: Any) -> bool:
|
57
|
-
"""Checks whether a given object is a string which complies the python
|
58
|
-
variable syntax.
|
59
|
-
"""
|
60
|
-
if not isinstance(name, str) or keyword.iskeyword(name):
|
61
|
-
return False
|
62
|
-
name_cleaned = "".join(re.findall(ALLOWED_VAR_SYNTAX, name))
|
63
|
-
return name_cleaned == name
|
64
|
-
|
65
|
-
|
66
|
-
class MetaDict(MutableMapping[KT, VT], dict):
|
67
|
-
"""Class that extends `dict` to access and assign keys via attribute dot
|
68
|
-
notation.
|
69
|
-
|
70
|
-
Examples:
|
71
|
-
>>> d = MetaDict({'foo': {'bar': [{'a': 1}, {'a': 2}]}})
|
72
|
-
>>> d.foo.bar[1].a
|
73
|
-
2
|
74
|
-
>>> d["foo"]["bar"][1]["a"]
|
75
|
-
2
|
76
|
-
>>> d.bar = 'demo'
|
77
|
-
>>> d.bar
|
78
|
-
'demo'
|
79
|
-
|
80
|
-
`MetaDict` inherits from MutableMapping to avoid overwriting all `dict`
|
81
|
-
methods. In addition, it inherits from `dict` to pass the quite common
|
82
|
-
`isinstance(obj, dict) check.
|
83
|
-
|
84
|
-
Also, inheriting from `dict` enables json encoding/decoding without a
|
85
|
-
custom encoder.
|
86
|
-
"""
|
87
|
-
|
88
|
-
def __init__(self, *args, nested_assign: bool = False, **kwargs) -> None:
|
89
|
-
# NOTE: check that 'nested_assign' is of type bool
|
90
|
-
if not isinstance(nested_assign, bool):
|
91
|
-
raise TypeError(
|
92
|
-
"Keyword argument 'nested_assign' must be an instance of "
|
93
|
-
"type 'bool'"
|
94
|
-
)
|
95
|
-
|
96
|
-
# NOTE: init internal attributes and data store
|
97
|
-
self.__dict__["_data"]: dict[KT, VT] = {}
|
98
|
-
self.__dict__["_nested_assign"] = nested_assign
|
99
|
-
self.__dict__["_parent"] = kwargs.pop("_parent", None)
|
100
|
-
self.__dict__["_key"] = kwargs.pop("_key", None)
|
101
|
-
|
102
|
-
# update state of data store
|
103
|
-
self.update(*args, **kwargs)
|
104
|
-
|
105
|
-
# call `dict` constructor with stored data to enable object encoding
|
106
|
-
# (e.g. `json.dumps()`) that relies on `dict`
|
107
|
-
dict.__init__(self, self._data)
|
108
|
-
|
109
|
-
def __len__(self) -> int:
|
110
|
-
return len(self._data)
|
111
|
-
|
112
|
-
def __iter__(self) -> Iterator[KT]:
|
113
|
-
return iter(self._data)
|
114
|
-
|
115
|
-
def __setitem__(self, key: KT, value: VT) -> None:
|
116
|
-
# show a warning if the assigned key or attribute is used internally
|
117
|
-
# (e.g `items`, `keys`, etc.)
|
118
|
-
try:
|
119
|
-
self.__getattribute__(key)
|
120
|
-
key_is_protected = True
|
121
|
-
except (AttributeError, TypeError):
|
122
|
-
key_is_protected = False
|
123
|
-
if key_is_protected:
|
124
|
-
warnings.warn(
|
125
|
-
f"'{self.__class__.__name__}' object uses '{key}' internally. "
|
126
|
-
f"'{key}' can only be accessed via `obj['{key}']`.",
|
127
|
-
stacklevel=2,
|
128
|
-
)
|
129
|
-
|
130
|
-
# set key recursively
|
131
|
-
self._data[key] = self._from_object(value)
|
132
|
-
|
133
|
-
# update parent when nested keys or attributes are assigned
|
134
|
-
parent = self.__dict__.pop("_parent", None)
|
135
|
-
key = self.__dict__.get("_key", None)
|
136
|
-
if parent is not None:
|
137
|
-
parent[key] = self._data
|
138
|
-
|
139
|
-
def __getitem__(self, key: KT) -> VT:
|
140
|
-
try:
|
141
|
-
value = self._data[key]
|
142
|
-
except KeyError:
|
143
|
-
if self.nested_assign:
|
144
|
-
return self.__missing__(key)
|
145
|
-
raise
|
146
|
-
|
147
|
-
return value
|
148
|
-
|
149
|
-
def __missing__(self, key: KT) -> Self:
|
150
|
-
return self.__class__(
|
151
|
-
_parent=self, _key=key, nested_assign=self._nested_assign
|
152
|
-
)
|
153
|
-
|
154
|
-
def __delitem__(self, key: KT) -> None:
|
155
|
-
del self._data[key]
|
156
|
-
|
157
|
-
def __setattr__(self, attr: str, val: VT) -> None:
|
158
|
-
self[attr] = val
|
159
|
-
|
160
|
-
def __getattr__(self, key: KT) -> VT:
|
161
|
-
try:
|
162
|
-
return self[key]
|
163
|
-
except KeyError:
|
164
|
-
raise AttributeError(
|
165
|
-
f"'{self.__class__.__name__}' object has no attribute '{key}'"
|
166
|
-
) from None
|
167
|
-
|
168
|
-
def __delattr__(self, key: KT) -> None:
|
169
|
-
try:
|
170
|
-
del self[key]
|
171
|
-
except KeyError:
|
172
|
-
raise AttributeError(
|
173
|
-
f"'{self.__class__.__name__}' object has no attribute '{key}'"
|
174
|
-
) from None
|
175
|
-
|
176
|
-
def __str__(self) -> str:
|
177
|
-
return str(self._data)
|
178
|
-
|
179
|
-
def __repr__(self) -> str:
|
180
|
-
return repr(self._data)
|
181
|
-
|
182
|
-
@staticmethod
|
183
|
-
def repack_args(cls: type, state: dict) -> MetaDict:
|
184
|
-
"""Repack and rename keyword arguments stored in state before feeding
|
185
|
-
to class constructor
|
186
|
-
"""
|
187
|
-
_data = state.pop("_data")
|
188
|
-
_nested_assign = state.pop("_nested_assign")
|
189
|
-
return cls(_data, nested_assign=_nested_assign, **state)
|
190
|
-
|
191
|
-
def __reduce__(self) -> tuple:
|
192
|
-
"""Return state information for pickling."""
|
193
|
-
return MetaDict.repack_args, (self.__class__, self.__dict__)
|
194
|
-
|
195
|
-
def __dir__(self) -> Iterable[str]:
|
196
|
-
"""Extend dir list with accessible dict keys (enables autocompletion
|
197
|
-
when using dot notation)
|
198
|
-
"""
|
199
|
-
dict_keys = [
|
200
|
-
key for key in self._data.keys() if complies_variable_syntax(key)
|
201
|
-
]
|
202
|
-
return dir(type(self)) + dict_keys
|
203
|
-
|
204
|
-
def copy(self) -> Self:
|
205
|
-
return self.__copy__()
|
206
|
-
|
207
|
-
def __copy__(self) -> Self:
|
208
|
-
cls = self.__class__
|
209
|
-
result = cls.__new__(cls)
|
210
|
-
result.__dict__.update(
|
211
|
-
{k: copy.copy(v) for k, v in self.__dict__.items()}
|
212
|
-
)
|
213
|
-
return result
|
214
|
-
|
215
|
-
@classmethod
|
216
|
-
def fromkeys(
|
217
|
-
cls,
|
218
|
-
iterable: Iterable[KT],
|
219
|
-
value: Optional[VT] = None,
|
220
|
-
) -> Self:
|
221
|
-
"""Constructor MetaDict form keys iterator.
|
222
|
-
|
223
|
-
Examples:
|
224
|
-
>>> def iter_keys() -> Iterable[str]:
|
225
|
-
... for i in range(3):
|
226
|
-
... yield f"k{i}"
|
227
|
-
>>> MetaDict.fromkeys(iterable=iter_keys())
|
228
|
-
{'k0': None, 'k1': None, 'k2': None}
|
229
|
-
"""
|
230
|
-
return cls({key: value for key in iterable})
|
231
|
-
|
232
|
-
def to_dict(self) -> dict:
|
233
|
-
return MetaDict._to_object(self._data)
|
234
|
-
|
235
|
-
@staticmethod
|
236
|
-
def _to_object(obj: Any) -> Any:
|
237
|
-
"""Recursively converts all nested MetaDicts to dicts."""
|
238
|
-
|
239
|
-
if isinstance(obj, (list, tuple, set)):
|
240
|
-
if MetaDict._contains_mapping(obj):
|
241
|
-
value = type(obj)(MetaDict._to_object(x) for x in obj)
|
242
|
-
else:
|
243
|
-
value = obj
|
244
|
-
elif isinstance(obj, Mapping):
|
245
|
-
value = {k: MetaDict._to_object(v) for k, v in obj.items()}
|
246
|
-
else:
|
247
|
-
value = obj
|
248
|
-
|
249
|
-
return value
|
250
|
-
|
251
|
-
def _from_object(self, obj: Any) -> Any:
|
252
|
-
"""Recursively converts all nested dicts to MetaDicts."""
|
253
|
-
|
254
|
-
if isinstance(obj, (list, tuple, set)):
|
255
|
-
if MetaDict._contains_mapping(obj):
|
256
|
-
value = type(obj)(self._from_object(x) for x in obj)
|
257
|
-
else:
|
258
|
-
value = obj
|
259
|
-
elif isinstance(obj, MetaDict):
|
260
|
-
value = obj
|
261
|
-
elif isinstance(obj, Mapping):
|
262
|
-
value = self.__class__(
|
263
|
-
{k: self._from_object(v) for k, v in obj.items()},
|
264
|
-
nested_assign=self._nested_assign,
|
265
|
-
)
|
266
|
-
else:
|
267
|
-
value = obj
|
268
|
-
|
269
|
-
return value
|
270
|
-
|
271
|
-
def _set_nested_assignment(self, val: bool):
|
272
|
-
self.__dict__["_nested_assign"] = val
|
273
|
-
for value in self.values():
|
274
|
-
if isinstance(value, (list, tuple, set)):
|
275
|
-
for elem in value:
|
276
|
-
if isinstance(elem, MetaDict):
|
277
|
-
elem._set_nested_assignment(val)
|
278
|
-
elif isinstance(value, MetaDict):
|
279
|
-
value._set_nested_assignment(val)
|
280
|
-
|
281
|
-
def enable_nested_assignment(self):
|
282
|
-
self._set_nested_assignment(True)
|
283
|
-
|
284
|
-
def disable_nested_assignment(self):
|
285
|
-
self._set_nested_assignment(False)
|
286
|
-
|
287
|
-
@contextlib.contextmanager
|
288
|
-
def enabling_nested_assignment(self):
|
289
|
-
"""Context manager which temporarily enables nested key/attribute
|
290
|
-
assignment.
|
291
|
-
"""
|
292
|
-
nested_assign = self.nested_assign
|
293
|
-
if not nested_assign:
|
294
|
-
self.enable_nested_assignment()
|
295
|
-
try:
|
296
|
-
yield self
|
297
|
-
finally:
|
298
|
-
if not nested_assign:
|
299
|
-
self.disable_nested_assignment()
|
300
|
-
|
301
|
-
@property
|
302
|
-
def nested_assign(self):
|
303
|
-
return self._nested_assign
|
304
|
-
|
305
|
-
@staticmethod
|
306
|
-
def _contains_mapping(
|
307
|
-
iterable: Iterable, ignore: Optional[type] = None
|
308
|
-
) -> bool:
|
309
|
-
"""Recursively checks whether an Iterable contains an instance of
|
310
|
-
Mapping.
|
311
|
-
"""
|
312
|
-
for x in iterable:
|
313
|
-
if isinstance(x, Mapping):
|
314
|
-
if ignore is None or not isinstance(x, ignore):
|
315
|
-
return True
|
316
|
-
elif isinstance(x, (list, set, tuple)):
|
317
|
-
return MetaDict._contains_mapping(x, ignore)
|
318
|
-
return False
|
319
|
-
|
320
|
-
# NOTE: Add the following inherited methods from collections.abc.Mapping
|
321
|
-
# directly to make pycharm happy to checking.
|
322
|
-
# (removing an annoying warning for dict unpacking)
|
323
|
-
def __contains__(self, key):
|
324
|
-
try:
|
325
|
-
self[key]
|
326
|
-
except KeyError:
|
327
|
-
return False
|
328
|
-
else:
|
329
|
-
return True
|
330
|
-
|
331
|
-
def keys(self):
|
332
|
-
"""D.keys() -> a set-like object providing a view on D's keys"""
|
333
|
-
return KeysView(self)
|
File without changes
|
ddeutil/workflow/vendors/aws.py
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
from typing import Any, Optional
|
2
|
-
|
3
|
-
try:
|
4
|
-
import boto3
|
5
|
-
import botocore.exceptions
|
6
|
-
except ImportError:
|
7
|
-
raise ImportError(
|
8
|
-
"Please install boto3 package if want to use boto wrapped object.\n\t\t"
|
9
|
-
"$ pip install boto3"
|
10
|
-
) from None
|
11
|
-
|
12
|
-
|
13
|
-
class WrapBoto3:
|
14
|
-
"""Difference in boto3 between resource, client, and session
|
15
|
-
docs: https://stackoverflow.com/questions/42809096/
|
16
|
-
difference-in-boto3-between-resource-client-and-session
|
17
|
-
|
18
|
-
.. config::
|
19
|
-
|
20
|
-
~/.aws/credentials
|
21
|
-
|
22
|
-
[my-user]
|
23
|
-
aws_access_key_id = AKIAxxx
|
24
|
-
aws_secret_access_key = xxx
|
25
|
-
|
26
|
-
[my-role]
|
27
|
-
source_profile = my-user
|
28
|
-
role_arn = arn:aws:iam::123456789012:role/the-role
|
29
|
-
|
30
|
-
~/.aws/config
|
31
|
-
|
32
|
-
[profile my-role]
|
33
|
-
region = ap-southeast-2
|
34
|
-
"""
|
35
|
-
|
36
|
-
def __init__(
|
37
|
-
self,
|
38
|
-
access_key_id: str,
|
39
|
-
secret_access_key: str,
|
40
|
-
region_name: Optional[str] = None,
|
41
|
-
*,
|
42
|
-
role_session_name: Optional[str] = None,
|
43
|
-
role_arn: Optional[str] = None,
|
44
|
-
mfa_serial: Optional[str] = None,
|
45
|
-
):
|
46
|
-
self.access_key_id = access_key_id
|
47
|
-
self.secret_access_key = secret_access_key
|
48
|
-
self.region_name: str = region_name or "ap-southeast-1"
|
49
|
-
|
50
|
-
# Optional for session.
|
51
|
-
self.role_session_name: str = role_session_name or "AssumeRoleSession"
|
52
|
-
self.role_arn = role_arn
|
53
|
-
self.mfa_serial = mfa_serial
|
54
|
-
|
55
|
-
# Create credential
|
56
|
-
self.cred = self.make_cred()
|
57
|
-
|
58
|
-
def make_cred(self) -> dict[str, str]:
|
59
|
-
if self.role_arn is None:
|
60
|
-
return {
|
61
|
-
"AccessKeyId": self.access_key_id,
|
62
|
-
"SecretAccessKey": self.secret_access_key,
|
63
|
-
}
|
64
|
-
# NOTE: A low-level client representing AWS Security Token Service (STS)
|
65
|
-
# >>> sess = boto3.session.Session(
|
66
|
-
# ... aws_access_key_id=ARN_ACCESS_KEY,
|
67
|
-
# ... aws_secret_access_key=ARN_SECRET_KEY
|
68
|
-
# ... )
|
69
|
-
# >>> sts_client = sess.client('sts')
|
70
|
-
sts_client = boto3.client(
|
71
|
-
service_name="sts",
|
72
|
-
region_name=self.region_name,
|
73
|
-
aws_access_key_id=self.access_key_id,
|
74
|
-
aws_secret_access_key=self.secret_access_key,
|
75
|
-
)
|
76
|
-
mfa_optional: dict[str, str] = {}
|
77
|
-
if self.mfa_serial:
|
78
|
-
mfa_otp: str = str(input("Enter the MFA code: "))
|
79
|
-
mfa_optional = (
|
80
|
-
{"SerialNumber": self.mfa_serial, "TokenCode": mfa_otp},
|
81
|
-
)
|
82
|
-
assumed_role = sts_client.assume_role(
|
83
|
-
RoleArn=self.role_arn,
|
84
|
-
RoleSessionName=self.role_session_name,
|
85
|
-
DurationSeconds=3600,
|
86
|
-
**mfa_optional,
|
87
|
-
)
|
88
|
-
# NOTE: From the response that contains the assumed role, get the
|
89
|
-
# temporary credentials that can be used to make subsequent API calls
|
90
|
-
return assumed_role["Credentials"]
|
91
|
-
|
92
|
-
@property
|
93
|
-
def session(self):
|
94
|
-
"""Can use by
|
95
|
-
``s3 = self.session.client('s3')``
|
96
|
-
``s3 = self.session.resource('s3')``
|
97
|
-
"""
|
98
|
-
return boto3.session.Session(
|
99
|
-
aws_access_key_id=self.cred["AccessKeyId"],
|
100
|
-
aws_secret_access_key=self.cred["SecretAccessKey"],
|
101
|
-
aws_session_token=self.cred.get("SessionToken"),
|
102
|
-
)
|
103
|
-
|
104
|
-
@property
|
105
|
-
def s3(self):
|
106
|
-
return boto3.client(
|
107
|
-
service_name="s3",
|
108
|
-
region_name=self.region_name,
|
109
|
-
aws_access_key_id=self.cred["AccessKeyId"],
|
110
|
-
aws_secret_access_key=self.cred["SecretAccessKey"],
|
111
|
-
aws_session_token=self.cred.get("SessionToken"),
|
112
|
-
)
|
113
|
-
|
114
|
-
def list_objects(self, bucket: str, prefix: str):
|
115
|
-
objs: list[dict[str, Any]] = []
|
116
|
-
kwargs = {"Bucket": bucket, "Prefix": prefix}
|
117
|
-
while True:
|
118
|
-
resp = self.s3.list_objects_v2(**kwargs)
|
119
|
-
for obj in resp["Contents"]:
|
120
|
-
objs.append(obj)
|
121
|
-
try:
|
122
|
-
kwargs["ContinuationToken"] = resp["NextContinuationToken"]
|
123
|
-
except KeyError:
|
124
|
-
break
|
125
|
-
return objs
|
126
|
-
|
127
|
-
def paginate(
|
128
|
-
self,
|
129
|
-
bucket: str,
|
130
|
-
prefix: str,
|
131
|
-
*,
|
132
|
-
marker: Optional[str] = None,
|
133
|
-
search: Optional[str] = None,
|
134
|
-
):
|
135
|
-
"""
|
136
|
-
.. docs:
|
137
|
-
- https://boto3.amazonaws.com/v1/documentation/api/latest/
|
138
|
-
guide/paginators.html
|
139
|
-
|
140
|
-
.. search::
|
141
|
-
- "Contents[?Size > `100`][]"
|
142
|
-
- "Contents[?contains(LastModified, `'"2022-01-01"'`)]"
|
143
|
-
- "Contents[?LastModified>=`YYYY-MM-DD`].Key"
|
144
|
-
- "DeleteMarkers[?LastModified>=`2020-07-07T00:00:00`
|
145
|
-
&& IsLatest==`true`].[Key,VersionId]"
|
146
|
-
"""
|
147
|
-
paginator = self.s3.get_paginator("list_objects_v2")
|
148
|
-
page_iterator = paginator.paginate(
|
149
|
-
Bucket=bucket,
|
150
|
-
Prefix=prefix,
|
151
|
-
PaginationConfig={
|
152
|
-
# 'MaxItems': 10,
|
153
|
-
"PageSize": 10,
|
154
|
-
"StartingToken": marker,
|
155
|
-
},
|
156
|
-
)
|
157
|
-
|
158
|
-
for page in page_iterator:
|
159
|
-
print("# This is new page")
|
160
|
-
print("Contents Count:", len(page["Contents"]))
|
161
|
-
if "NextContinuationToken" in page.keys():
|
162
|
-
print(page["NextContinuationToken"])
|
163
|
-
|
164
|
-
# filtered_iterator = page_iterator.search("Contents[?Size > `100`][]")
|
165
|
-
# for key_data in filtered_iterator:
|
166
|
-
# print(key_data)
|
167
|
-
|
168
|
-
# page_iterator = paginator.paginate(
|
169
|
-
# Bucket=bucket,
|
170
|
-
# Prefix=prefix,
|
171
|
-
# PaginationConfig={
|
172
|
-
# 'MaxItems': 10,
|
173
|
-
# 'PageSize': 10,
|
174
|
-
# 'StartingToken': marker
|
175
|
-
# }
|
176
|
-
# )
|
177
|
-
|
178
|
-
def exists(self, bucket: str, prefix: str) -> bool:
|
179
|
-
try:
|
180
|
-
self.s3.head_object(Bucket=bucket, Key=prefix)
|
181
|
-
return True
|
182
|
-
except botocore.exceptions.ClientError as err:
|
183
|
-
if err.response["Error"]["Code"]:
|
184
|
-
return False
|
185
|
-
raise
|
ddeutil/workflow/vendors/az.py
DELETED
File without changes
|
ddeutil/workflow/vendors/pd.py
DELETED
ddeutil/workflow/vendors/pg.py
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
# ------------------------------------------------------------------------------
|
2
|
-
# Copyright (c) 2022 Korawich Anuttra. All rights reserved.
|
3
|
-
# Licensed under the MIT License. See LICENSE in the project root for
|
4
|
-
# license information.
|
5
|
-
# ------------------------------------------------------------------------------
|
6
|
-
from __future__ import annotations
|
7
|
-
|
8
|
-
from .__dataset import TblDataset
|
9
|
-
|
10
|
-
|
11
|
-
class PostgresTbl(TblDataset): ...
|