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.
@@ -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
@@ -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
File without changes
@@ -1,11 +0,0 @@
1
- class WarpMinio:
2
-
3
- def __init__(
4
- self,
5
- host: str,
6
- access_key: str,
7
- secret_access_key: str,
8
- ):
9
- self.host: str = host
10
- self.access_key: str = access_key
11
- self.secret_access_key: str = secret_access_key
@@ -1,13 +0,0 @@
1
- class PandasCSV: ...
2
-
3
-
4
- class PandasJson: ...
5
-
6
-
7
- class PandasParq: ...
8
-
9
-
10
- class PandasDb: ...
11
-
12
-
13
- class PandasExcel: ...
@@ -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): ...