amapy-utils 1.0.0__tar.gz
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.
- amapy-utils-1.0.0/PKG-INFO +25 -0
- amapy-utils-1.0.0/README.md +1 -0
- amapy-utils-1.0.0/amapy_utils.egg-info/PKG-INFO +25 -0
- amapy-utils-1.0.0/amapy_utils.egg-info/SOURCES.txt +45 -0
- amapy-utils-1.0.0/amapy_utils.egg-info/dependency_links.txt +1 -0
- amapy-utils-1.0.0/amapy_utils.egg-info/entry_points.txt +2 -0
- amapy-utils-1.0.0/amapy_utils.egg-info/requires.txt +14 -0
- amapy-utils-1.0.0/amapy_utils.egg-info/top_level.txt +1 -0
- amapy-utils-1.0.0/asset_utils/__init__.py +0 -0
- amapy-utils-1.0.0/asset_utils/common/__init__.py +7 -0
- amapy-utils-1.0.0/asset_utils/common/better_set.py +86 -0
- amapy-utils-1.0.0/asset_utils/common/class_property.py +31 -0
- amapy-utils-1.0.0/asset_utils/common/exceptions.py +286 -0
- amapy-utils-1.0.0/asset_utils/common/singleton.py +35 -0
- amapy-utils-1.0.0/asset_utils/common/tests/__init__.py +0 -0
- amapy-utils-1.0.0/asset_utils/common/tests/test_better_set.py +63 -0
- amapy-utils-1.0.0/asset_utils/common/tests/test_class_property.py +29 -0
- amapy-utils-1.0.0/asset_utils/common/tests/test_singleton.py +23 -0
- amapy-utils-1.0.0/asset_utils/common/user_commands.py +273 -0
- amapy-utils-1.0.0/asset_utils/common/user_messages.py +10 -0
- amapy-utils-1.0.0/asset_utils/utils/__init__.py +2 -0
- amapy-utils-1.0.0/asset_utils/utils/aws_hash.py +147 -0
- amapy-utils-1.0.0/asset_utils/utils/cloud_utils.py +161 -0
- amapy-utils-1.0.0/asset_utils/utils/file_html_diff.py +24 -0
- amapy-utils-1.0.0/asset_utils/utils/file_tree.py +53 -0
- amapy-utils-1.0.0/asset_utils/utils/file_utils.py +615 -0
- amapy-utils-1.0.0/asset_utils/utils/in_memory_file.py +58 -0
- amapy-utils-1.0.0/asset_utils/utils/in_memory_zip.py +27 -0
- amapy-utils-1.0.0/asset_utils/utils/log_utils.py +352 -0
- amapy-utils-1.0.0/asset_utils/utils/pager.py +43 -0
- amapy-utils-1.0.0/asset_utils/utils/path_utils.py +65 -0
- amapy-utils-1.0.0/asset_utils/utils/progress.py +99 -0
- amapy-utils-1.0.0/asset_utils/utils/stat_utils.py +17 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/__init__.py +0 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_aws_hash.py +86 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_cloud_utils.py +80 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_file_tree.py +24 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_file_utils.py +96 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_in_memory_file.py +66 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_log_utils.py +70 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_path_utils.py +27 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_progress.py +57 -0
- amapy-utils-1.0.0/asset_utils/utils/tests/test_utils.py +76 -0
- amapy-utils-1.0.0/asset_utils/utils/utils.py +380 -0
- amapy-utils-1.0.0/asset_utils/utils/web_utils.py +26 -0
- amapy-utils-1.0.0/pyproject.toml +40 -0
- amapy-utils-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: amapy-utils
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A utility package that provides common functionality shared across the asset-manager ecosystem. It serves as a foundation library offering various helper functions and tools needed by other components in the system.
|
|
5
|
+
Author-email: Swarup Mahanti <swarup.mahanti@roche.com>
|
|
6
|
+
Maintainer-email: Swarup Mahanti <swarup.mahanti@roche.com>
|
|
7
|
+
License: Copyright (c) 2024 Roche Diagnostics Computation Science & Informatics
|
|
8
|
+
Requires-Python: <3.11,>=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: aiofiles==0.7.*
|
|
11
|
+
Requires-Dist: cached_property==1.5.*
|
|
12
|
+
Requires-Dist: colorama==0.4.*
|
|
13
|
+
Requires-Dist: crcmod==1.*
|
|
14
|
+
Requires-Dist: psutil==5.9.*
|
|
15
|
+
Requires-Dist: pyopenssl==24.0.*
|
|
16
|
+
Requires-Dist: pypager==3.0.1
|
|
17
|
+
Requires-Dist: pytz==2021.3
|
|
18
|
+
Requires-Dist: pyyaml==6.0.*
|
|
19
|
+
Requires-Dist: requests==2.32.*
|
|
20
|
+
Requires-Dist: ruamel.yaml==0.18.*
|
|
21
|
+
Requires-Dist: speedtest-cli==2.1.*
|
|
22
|
+
Requires-Dist: tabulate==0.9.*
|
|
23
|
+
Requires-Dist: tqdm==4.62.*
|
|
24
|
+
|
|
25
|
+
asset-utils
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
asset-utils
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: amapy-utils
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A utility package that provides common functionality shared across the asset-manager ecosystem. It serves as a foundation library offering various helper functions and tools needed by other components in the system.
|
|
5
|
+
Author-email: Swarup Mahanti <swarup.mahanti@roche.com>
|
|
6
|
+
Maintainer-email: Swarup Mahanti <swarup.mahanti@roche.com>
|
|
7
|
+
License: Copyright (c) 2024 Roche Diagnostics Computation Science & Informatics
|
|
8
|
+
Requires-Python: <3.11,>=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: aiofiles==0.7.*
|
|
11
|
+
Requires-Dist: cached_property==1.5.*
|
|
12
|
+
Requires-Dist: colorama==0.4.*
|
|
13
|
+
Requires-Dist: crcmod==1.*
|
|
14
|
+
Requires-Dist: psutil==5.9.*
|
|
15
|
+
Requires-Dist: pyopenssl==24.0.*
|
|
16
|
+
Requires-Dist: pypager==3.0.1
|
|
17
|
+
Requires-Dist: pytz==2021.3
|
|
18
|
+
Requires-Dist: pyyaml==6.0.*
|
|
19
|
+
Requires-Dist: requests==2.32.*
|
|
20
|
+
Requires-Dist: ruamel.yaml==0.18.*
|
|
21
|
+
Requires-Dist: speedtest-cli==2.1.*
|
|
22
|
+
Requires-Dist: tabulate==0.9.*
|
|
23
|
+
Requires-Dist: tqdm==4.62.*
|
|
24
|
+
|
|
25
|
+
asset-utils
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
amapy_utils.egg-info/PKG-INFO
|
|
4
|
+
amapy_utils.egg-info/SOURCES.txt
|
|
5
|
+
amapy_utils.egg-info/dependency_links.txt
|
|
6
|
+
amapy_utils.egg-info/entry_points.txt
|
|
7
|
+
amapy_utils.egg-info/requires.txt
|
|
8
|
+
amapy_utils.egg-info/top_level.txt
|
|
9
|
+
asset_utils/__init__.py
|
|
10
|
+
asset_utils/common/__init__.py
|
|
11
|
+
asset_utils/common/better_set.py
|
|
12
|
+
asset_utils/common/class_property.py
|
|
13
|
+
asset_utils/common/exceptions.py
|
|
14
|
+
asset_utils/common/singleton.py
|
|
15
|
+
asset_utils/common/user_commands.py
|
|
16
|
+
asset_utils/common/user_messages.py
|
|
17
|
+
asset_utils/common/tests/__init__.py
|
|
18
|
+
asset_utils/common/tests/test_better_set.py
|
|
19
|
+
asset_utils/common/tests/test_class_property.py
|
|
20
|
+
asset_utils/common/tests/test_singleton.py
|
|
21
|
+
asset_utils/utils/__init__.py
|
|
22
|
+
asset_utils/utils/aws_hash.py
|
|
23
|
+
asset_utils/utils/cloud_utils.py
|
|
24
|
+
asset_utils/utils/file_html_diff.py
|
|
25
|
+
asset_utils/utils/file_tree.py
|
|
26
|
+
asset_utils/utils/file_utils.py
|
|
27
|
+
asset_utils/utils/in_memory_file.py
|
|
28
|
+
asset_utils/utils/in_memory_zip.py
|
|
29
|
+
asset_utils/utils/log_utils.py
|
|
30
|
+
asset_utils/utils/pager.py
|
|
31
|
+
asset_utils/utils/path_utils.py
|
|
32
|
+
asset_utils/utils/progress.py
|
|
33
|
+
asset_utils/utils/stat_utils.py
|
|
34
|
+
asset_utils/utils/utils.py
|
|
35
|
+
asset_utils/utils/web_utils.py
|
|
36
|
+
asset_utils/utils/tests/__init__.py
|
|
37
|
+
asset_utils/utils/tests/test_aws_hash.py
|
|
38
|
+
asset_utils/utils/tests/test_cloud_utils.py
|
|
39
|
+
asset_utils/utils/tests/test_file_tree.py
|
|
40
|
+
asset_utils/utils/tests/test_file_utils.py
|
|
41
|
+
asset_utils/utils/tests/test_in_memory_file.py
|
|
42
|
+
asset_utils/utils/tests/test_log_utils.py
|
|
43
|
+
asset_utils/utils/tests/test_path_utils.py
|
|
44
|
+
asset_utils/utils/tests/test_progress.py
|
|
45
|
+
asset_utils/utils/tests/test_utils.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
aiofiles==0.7.*
|
|
2
|
+
cached_property==1.5.*
|
|
3
|
+
colorama==0.4.*
|
|
4
|
+
crcmod==1.*
|
|
5
|
+
psutil==5.9.*
|
|
6
|
+
pyopenssl==24.0.*
|
|
7
|
+
pypager==3.0.1
|
|
8
|
+
pytz==2021.3
|
|
9
|
+
pyyaml==6.0.*
|
|
10
|
+
requests==2.32.*
|
|
11
|
+
ruamel.yaml==0.18.*
|
|
12
|
+
speedtest-cli==2.1.*
|
|
13
|
+
tabulate==0.9.*
|
|
14
|
+
tqdm==4.62.*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
asset_utils
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BetterSet:
|
|
5
|
+
"""Custom Set for Objects"""
|
|
6
|
+
|
|
7
|
+
def __init__(self, *args):
|
|
8
|
+
# self._dict = OrderedDict()
|
|
9
|
+
self._dict = {}
|
|
10
|
+
for arg in args:
|
|
11
|
+
self.add(arg)
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def items(self):
|
|
15
|
+
""" Return a list containing all items in sorted order, if possible """
|
|
16
|
+
return list(self._dict.keys())
|
|
17
|
+
|
|
18
|
+
def extend(self, args):
|
|
19
|
+
""" Add several items at once. """
|
|
20
|
+
for arg in args:
|
|
21
|
+
self.add(arg)
|
|
22
|
+
|
|
23
|
+
def add(self, item):
|
|
24
|
+
"""Add one item to set"""
|
|
25
|
+
if item in self._dict:
|
|
26
|
+
del self._dict[item]
|
|
27
|
+
self._dict[item] = item
|
|
28
|
+
|
|
29
|
+
def remove(self, item):
|
|
30
|
+
"""Remove item from set
|
|
31
|
+
this will raise a KeyError if item doesn't exist
|
|
32
|
+
"""
|
|
33
|
+
del self._dict[item]
|
|
34
|
+
|
|
35
|
+
def discard(self, item):
|
|
36
|
+
"""Removes an item for set
|
|
37
|
+
Doesn't raise error if item doesn't exist
|
|
38
|
+
"""
|
|
39
|
+
if item in self._dict:
|
|
40
|
+
self.remove(item)
|
|
41
|
+
|
|
42
|
+
def contains(self, item):
|
|
43
|
+
"""Check if the set contains item"""
|
|
44
|
+
return item in self._dict
|
|
45
|
+
|
|
46
|
+
def get(self, item):
|
|
47
|
+
"""returns the object matching the value of the item"""
|
|
48
|
+
return self._dict.get(item, None)
|
|
49
|
+
|
|
50
|
+
def clear(self):
|
|
51
|
+
"""clears all items in the set"""
|
|
52
|
+
self._dict = {}
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def first(self):
|
|
56
|
+
return self.items[0]
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def last(self):
|
|
60
|
+
return self.items[len(self.items) - 1]
|
|
61
|
+
|
|
62
|
+
def union(self, s2):
|
|
63
|
+
result = copy.copy(self)
|
|
64
|
+
for item in s2:
|
|
65
|
+
result.add(item)
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
# High-performance membership test
|
|
69
|
+
__contains__ = contains
|
|
70
|
+
|
|
71
|
+
def __repr__(self):
|
|
72
|
+
return f"{self.__class__.__name__}"
|
|
73
|
+
|
|
74
|
+
def __getitem__(self, index):
|
|
75
|
+
""" Support the 'for item in set:' protocol. """
|
|
76
|
+
return list(self._dict.keys())[index]
|
|
77
|
+
|
|
78
|
+
def __iter__(self):
|
|
79
|
+
""" convert to list """
|
|
80
|
+
return iter(self._dict)
|
|
81
|
+
|
|
82
|
+
def __len__(self):
|
|
83
|
+
return len(self._dict)
|
|
84
|
+
|
|
85
|
+
def __copy__(self):
|
|
86
|
+
return BetterSet(*self.items)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class ClassPropertyDescriptor(object):
|
|
2
|
+
"""Class Property Descriptor that allows adding property to class objects
|
|
3
|
+
based on: https://stackoverflow.com/questions/5189699/how-to-make-a-class-property
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
def __init__(self, fget, fset=None):
|
|
7
|
+
self.fget = fget
|
|
8
|
+
self.fset = fset
|
|
9
|
+
|
|
10
|
+
def __get__(self, obj, klass=None):
|
|
11
|
+
if klass is None:
|
|
12
|
+
klass = type(obj)
|
|
13
|
+
return self.fget.__get__(obj, klass)()
|
|
14
|
+
|
|
15
|
+
def __set__(self, obj, value):
|
|
16
|
+
if not self.fset:
|
|
17
|
+
raise AttributeError("can't set attribute")
|
|
18
|
+
type_ = type(obj)
|
|
19
|
+
return self.fset.__get__(obj, type_)(value)
|
|
20
|
+
|
|
21
|
+
def setter(self, func):
|
|
22
|
+
if not isinstance(func, (classmethod, staticmethod)):
|
|
23
|
+
func = classmethod(func)
|
|
24
|
+
self.fset = func
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def classproperty(func):
|
|
29
|
+
if not isinstance(func, (classmethod, staticmethod)):
|
|
30
|
+
func = classmethod(func)
|
|
31
|
+
return ClassPropertyDescriptor(func)
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
|
|
3
|
+
from asset_utils.utils.log_utils import LogData, colored_string, LogColors
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AssetException(Exception):
|
|
7
|
+
"""Base class for all asset exceptions."""
|
|
8
|
+
msg: str = None
|
|
9
|
+
data: dict = None
|
|
10
|
+
fatal: bool = True
|
|
11
|
+
|
|
12
|
+
def __init__(self, msg=None, data=None, fatal=True, *args):
|
|
13
|
+
self.msg = msg or self.__class__.msg
|
|
14
|
+
if not self.msg and args:
|
|
15
|
+
self.msg = args[0]
|
|
16
|
+
self.data = data or self.__class__.data
|
|
17
|
+
self.fatal = fatal
|
|
18
|
+
if not self.msg:
|
|
19
|
+
raise Exception("required param missing: msg")
|
|
20
|
+
super().__init__(msg, data, *args)
|
|
21
|
+
|
|
22
|
+
@cached_property
|
|
23
|
+
def logs(self):
|
|
24
|
+
return LogData()
|
|
25
|
+
|
|
26
|
+
@cached_property
|
|
27
|
+
def log_colors(self):
|
|
28
|
+
return LogColors
|
|
29
|
+
|
|
30
|
+
def stop_execution(self):
|
|
31
|
+
self.__class__.stop_process()
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
msg = colored_string(f"error: {self.msg}", LogColors.ALERT)
|
|
35
|
+
extras = self.logs.print_format()
|
|
36
|
+
if extras:
|
|
37
|
+
msg += f"\n{extras}" if msg else extras
|
|
38
|
+
return msg
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def stop_process(cls):
|
|
42
|
+
exit(1)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AssetObjectConflictException(AssetException):
|
|
46
|
+
def __init__(self, b1, b2):
|
|
47
|
+
msg = f'Input type `{b1.unit}` provided by {b1.__name__} already registered by {b2.__name__}'
|
|
48
|
+
super().__init__(msg=msg)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class InvalidStorageURLError(AssetException):
|
|
52
|
+
msg = "Invalid storage url"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class InvalidStorageBackendError(AssetException):
|
|
56
|
+
msg = "Invalid storage backend, this blob store is not supported"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class InvalidStorageCredentialsError(AssetException):
|
|
60
|
+
msg = "Invalid storage credentials, please check your credentials and try again"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class NoActiveProjectError(AssetException):
|
|
64
|
+
msg = "There is no active project set, please select an active project"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class InvalidProjectError(AssetException):
|
|
68
|
+
msg = "project not found"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class InvalidRemoteURLError(AssetException):
|
|
72
|
+
msg = "remote_url is missing, please login again to retry"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class RemoteStorageError(AssetException):
|
|
76
|
+
msg = "remote storage for asset not configured yet"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class InvalidCredentialError(AssetException):
|
|
80
|
+
msg = "Invalid application credentials, please sign-in to update your credentials"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class InsufficientCredentialError(AssetException):
|
|
84
|
+
msg = "You don't have sufficient permission to perform this operation"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ContentNotAvailableError(AssetException):
|
|
88
|
+
msg = "Content not available locally"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ForbiddenRefError(AssetException):
|
|
92
|
+
"""thrown in case of forbidden references for example,
|
|
93
|
+
self refs
|
|
94
|
+
"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class InvalidAliasError(AssetException):
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class InvalidTagError(AssetException):
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class AssetStoreCreateError(AssetException):
|
|
107
|
+
"""throw if asset store is invalid"""
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class AssetStoreInvalidError(AssetException):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class InvalidArgumentError(ValueError, AssetException):
|
|
116
|
+
"""Thrown if arguments are invalid."""
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class InvalidAssetNameError(AssetException):
|
|
121
|
+
"""Thrown if the asset name passed by the user is invalid"""
|
|
122
|
+
msg = "invalid asset name"
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class AssetNotFoundError(AssetException):
|
|
127
|
+
msg = "asset not found locally"
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class InvalidObjectSourceError(AssetException):
|
|
132
|
+
msg = "file not found"
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class UnSupportedOperation(AssetException):
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class StagePathAsOutputError(AssetException):
|
|
141
|
+
"""Thrown if directory that stage is going to be saved in is specified as
|
|
142
|
+
an output of another stage.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
stage (Stage): a stage that is in some other stages output
|
|
146
|
+
output (str): an output covering the stage above
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __init__(self, stage, output):
|
|
150
|
+
assert isinstance(output, str)
|
|
151
|
+
super().__init__(
|
|
152
|
+
"{stage} is within an output '{output}' of another stage".format(
|
|
153
|
+
stage=stage, output=output
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class CircularDependencyError(AssetException):
|
|
159
|
+
"""Thrown if a file/directory specified both as an output and as a
|
|
160
|
+
dependency.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
dependency (str): path to the dependency.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
def __init__(self, dependency):
|
|
167
|
+
assert isinstance(dependency, str)
|
|
168
|
+
|
|
169
|
+
msg = "'{}' is specified as an output and as a dependency."
|
|
170
|
+
super().__init__(msg.format(dependency))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ArgumentDuplicationError(AssetException):
|
|
174
|
+
"""Thrown if a file/directory is specified as a dependency/output more
|
|
175
|
+
than once.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
path (str): path to the file/directory.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
def __init__(self, path):
|
|
182
|
+
assert isinstance(path, str)
|
|
183
|
+
super().__init__(f"file '{path}' is specified more than once.")
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class MoveNotDataSourceError(AssetException):
|
|
187
|
+
"""Thrown when trying to move a file/directory that is not an output
|
|
188
|
+
in a data source stage.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
path (str): path to the file/directory.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
def __init__(self, path):
|
|
195
|
+
msg = (
|
|
196
|
+
"move is not permitted for stages that are not data sources. "
|
|
197
|
+
"You need to either move '{path}' to a new location and edit "
|
|
198
|
+
"it by hand, or remove '{path}' and create a new one at the "
|
|
199
|
+
"desired location."
|
|
200
|
+
)
|
|
201
|
+
super().__init__(msg.format(path=path))
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class NotAssetRepoError(AssetException):
|
|
205
|
+
"""Thrown if a directory is not a BaseAsset repo"""
|
|
206
|
+
pass
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class DuplicateRepoError(AssetException):
|
|
210
|
+
"""Thrown if two repos with the same id exist """
|
|
211
|
+
msg = "Multiple repos found with the same repo id"
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class RepoOverwriteError(AssetException):
|
|
215
|
+
msg = "Possible Repo override detected - existing repo id found"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class RepoMovedError(AssetException):
|
|
219
|
+
msg = "Repo was moved from its original location"
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class NestedRepoError(AssetException):
|
|
223
|
+
msg = "Nested repo detected"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class InvalidVersionError(AssetException):
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class InvalidSeqIdError(AssetException):
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class InvalidInputError(AssetException):
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class ServerNotAvailableError(AssetException):
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class ServerUrlNotSetError(AssetException):
|
|
243
|
+
msg = "asset-server URL not set"
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class ResourceDownloadError(AssetException):
|
|
247
|
+
msg = "failed to download resource"
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class InvalidRefError(AssetException):
|
|
251
|
+
pass
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class AssetClassNotFoundError(AssetException):
|
|
255
|
+
msg = "asset class not found"
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class ClassListNotFoundError(AssetException):
|
|
259
|
+
msg = "asset-class list not found"
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class IncorrectServerResponseError(AssetException):
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class AssetParserError(AssetException):
|
|
267
|
+
"""Base class for CLI parser errors."""
|
|
268
|
+
|
|
269
|
+
def __init__(self):
|
|
270
|
+
super().__init__("parser error")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class BackoffRetryError(AssetException):
|
|
274
|
+
msg = "retry immediately"
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class GroupRetryError(AssetException):
|
|
278
|
+
msg = "retry in group"
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class ReadOnlyAssetError(AssetException):
|
|
282
|
+
msg = "read-only asset, change tracking and updates are disabled"
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class AssetSnapshotError(AssetException):
|
|
286
|
+
msg = "error while handling asset snapshot"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Singleton:
|
|
5
|
+
__metaclass__ = abc.ABCMeta
|
|
6
|
+
|
|
7
|
+
def __new__(cls):
|
|
8
|
+
raise Exception("singleton, used class.shared() instead")
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def shared(cls, **kwargs):
|
|
12
|
+
if cls is Singleton:
|
|
13
|
+
raise Exception("abstract class, you must inherit")
|
|
14
|
+
if not hasattr(cls, 'instance') or not getattr(cls, 'instance'):
|
|
15
|
+
instance = super(Singleton, cls).__new__(cls)
|
|
16
|
+
instance.post_init(**kwargs)
|
|
17
|
+
cls.instance = instance
|
|
18
|
+
return cls.instance
|
|
19
|
+
|
|
20
|
+
def post_init(self, **kwargs):
|
|
21
|
+
"""subclass must implement and do any custom initialization here
|
|
22
|
+
- we do this here to not let users make the mistake of implementing __init__
|
|
23
|
+
- __init__ is called everytime the user tries to instantiate
|
|
24
|
+
the class, even when we override __new__ as obove
|
|
25
|
+
- this is counterintuitive to how we perceive singletons in other languages
|
|
26
|
+
"""
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def de_init(cls):
|
|
31
|
+
"""
|
|
32
|
+
need a de_init so that we can test different inheritances of the singleton
|
|
33
|
+
:return:
|
|
34
|
+
"""
|
|
35
|
+
setattr(cls, 'instance', None)
|
|
File without changes
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from asset_utils.common.better_set import BetterSet
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_initialization():
|
|
7
|
+
# Test initialization with no items
|
|
8
|
+
empty_set = BetterSet()
|
|
9
|
+
assert len(empty_set) == 0
|
|
10
|
+
|
|
11
|
+
# Test initialization with one item
|
|
12
|
+
single_item_set = BetterSet("item1")
|
|
13
|
+
assert "item1" in single_item_set
|
|
14
|
+
|
|
15
|
+
# Test initialization with multiple items
|
|
16
|
+
multi_item_set = BetterSet("item1", "item2")
|
|
17
|
+
assert len(multi_item_set) == 2
|
|
18
|
+
assert "item1" in multi_item_set and "item2" in multi_item_set
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_add_extend():
|
|
22
|
+
test_set = BetterSet()
|
|
23
|
+
test_set.add("item1")
|
|
24
|
+
assert "item1" in test_set
|
|
25
|
+
# Test extend with a duplicate item
|
|
26
|
+
test_set.extend(["item1", "item2"])
|
|
27
|
+
assert "item1" in test_set and "item2" in test_set
|
|
28
|
+
assert len(test_set) == 2
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_remove_discard():
|
|
32
|
+
test_set = BetterSet("item1", "item2")
|
|
33
|
+
test_set.remove("item1")
|
|
34
|
+
assert "item1" not in test_set
|
|
35
|
+
# Test discard does not raise an error if item does not exist
|
|
36
|
+
test_set.discard("item3")
|
|
37
|
+
assert "item3" not in test_set
|
|
38
|
+
# Test remove raises KeyError if item does not exist
|
|
39
|
+
with pytest.raises(KeyError):
|
|
40
|
+
test_set.remove("item3")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_contains_get():
|
|
44
|
+
test_set = BetterSet("item1")
|
|
45
|
+
assert test_set.contains("item1")
|
|
46
|
+
assert not test_set.contains("item2")
|
|
47
|
+
|
|
48
|
+
assert test_set.get("item1") == "item1"
|
|
49
|
+
assert test_set.get("item2") is None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_clear():
|
|
53
|
+
test_set = BetterSet("item1", "item2")
|
|
54
|
+
test_set.clear()
|
|
55
|
+
assert len(test_set) == 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_union():
|
|
59
|
+
set1 = BetterSet("item1", "item2")
|
|
60
|
+
set2 = BetterSet("item2", "item3")
|
|
61
|
+
union_set = set1.union(set2)
|
|
62
|
+
assert len(union_set) == 3
|
|
63
|
+
assert "item1" in union_set and "item2" in union_set and "item3" in union_set
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from asset_utils.common.class_property import classproperty
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_class_property():
|
|
5
|
+
class Bar(object):
|
|
6
|
+
_bar = 1
|
|
7
|
+
|
|
8
|
+
@classproperty
|
|
9
|
+
def bar(cls):
|
|
10
|
+
return cls._bar
|
|
11
|
+
|
|
12
|
+
@bar.setter
|
|
13
|
+
def bar(cls, value):
|
|
14
|
+
cls._bar = value
|
|
15
|
+
|
|
16
|
+
# test instance creation
|
|
17
|
+
foo = Bar()
|
|
18
|
+
assert foo.bar == 1
|
|
19
|
+
|
|
20
|
+
baz = Bar()
|
|
21
|
+
assert baz.bar == 1
|
|
22
|
+
|
|
23
|
+
# classproperty getter
|
|
24
|
+
assert Bar.bar == 1
|
|
25
|
+
|
|
26
|
+
# classproperty setter
|
|
27
|
+
Bar.bar = 50
|
|
28
|
+
assert foo.bar == 50
|
|
29
|
+
assert baz.bar == 50
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from asset_utils.common.singleton import Singleton
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SingletonChild(Singleton):
|
|
5
|
+
def post_init(self):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SingletonChild2(Singleton):
|
|
10
|
+
def post_init(self):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_singleton():
|
|
15
|
+
child = SingletonChild.shared()
|
|
16
|
+
child_copy = SingletonChild.shared()
|
|
17
|
+
child2 = SingletonChild2.shared()
|
|
18
|
+
child2_copy = SingletonChild2.shared()
|
|
19
|
+
assert child == child_copy
|
|
20
|
+
assert child2 == child2_copy
|
|
21
|
+
assert child != child2
|
|
22
|
+
assert child.__class__ is SingletonChild
|
|
23
|
+
assert child2.__class__ is SingletonChild2
|