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.
Files changed (47) hide show
  1. amapy-utils-1.0.0/PKG-INFO +25 -0
  2. amapy-utils-1.0.0/README.md +1 -0
  3. amapy-utils-1.0.0/amapy_utils.egg-info/PKG-INFO +25 -0
  4. amapy-utils-1.0.0/amapy_utils.egg-info/SOURCES.txt +45 -0
  5. amapy-utils-1.0.0/amapy_utils.egg-info/dependency_links.txt +1 -0
  6. amapy-utils-1.0.0/amapy_utils.egg-info/entry_points.txt +2 -0
  7. amapy-utils-1.0.0/amapy_utils.egg-info/requires.txt +14 -0
  8. amapy-utils-1.0.0/amapy_utils.egg-info/top_level.txt +1 -0
  9. amapy-utils-1.0.0/asset_utils/__init__.py +0 -0
  10. amapy-utils-1.0.0/asset_utils/common/__init__.py +7 -0
  11. amapy-utils-1.0.0/asset_utils/common/better_set.py +86 -0
  12. amapy-utils-1.0.0/asset_utils/common/class_property.py +31 -0
  13. amapy-utils-1.0.0/asset_utils/common/exceptions.py +286 -0
  14. amapy-utils-1.0.0/asset_utils/common/singleton.py +35 -0
  15. amapy-utils-1.0.0/asset_utils/common/tests/__init__.py +0 -0
  16. amapy-utils-1.0.0/asset_utils/common/tests/test_better_set.py +63 -0
  17. amapy-utils-1.0.0/asset_utils/common/tests/test_class_property.py +29 -0
  18. amapy-utils-1.0.0/asset_utils/common/tests/test_singleton.py +23 -0
  19. amapy-utils-1.0.0/asset_utils/common/user_commands.py +273 -0
  20. amapy-utils-1.0.0/asset_utils/common/user_messages.py +10 -0
  21. amapy-utils-1.0.0/asset_utils/utils/__init__.py +2 -0
  22. amapy-utils-1.0.0/asset_utils/utils/aws_hash.py +147 -0
  23. amapy-utils-1.0.0/asset_utils/utils/cloud_utils.py +161 -0
  24. amapy-utils-1.0.0/asset_utils/utils/file_html_diff.py +24 -0
  25. amapy-utils-1.0.0/asset_utils/utils/file_tree.py +53 -0
  26. amapy-utils-1.0.0/asset_utils/utils/file_utils.py +615 -0
  27. amapy-utils-1.0.0/asset_utils/utils/in_memory_file.py +58 -0
  28. amapy-utils-1.0.0/asset_utils/utils/in_memory_zip.py +27 -0
  29. amapy-utils-1.0.0/asset_utils/utils/log_utils.py +352 -0
  30. amapy-utils-1.0.0/asset_utils/utils/pager.py +43 -0
  31. amapy-utils-1.0.0/asset_utils/utils/path_utils.py +65 -0
  32. amapy-utils-1.0.0/asset_utils/utils/progress.py +99 -0
  33. amapy-utils-1.0.0/asset_utils/utils/stat_utils.py +17 -0
  34. amapy-utils-1.0.0/asset_utils/utils/tests/__init__.py +0 -0
  35. amapy-utils-1.0.0/asset_utils/utils/tests/test_aws_hash.py +86 -0
  36. amapy-utils-1.0.0/asset_utils/utils/tests/test_cloud_utils.py +80 -0
  37. amapy-utils-1.0.0/asset_utils/utils/tests/test_file_tree.py +24 -0
  38. amapy-utils-1.0.0/asset_utils/utils/tests/test_file_utils.py +96 -0
  39. amapy-utils-1.0.0/asset_utils/utils/tests/test_in_memory_file.py +66 -0
  40. amapy-utils-1.0.0/asset_utils/utils/tests/test_log_utils.py +70 -0
  41. amapy-utils-1.0.0/asset_utils/utils/tests/test_path_utils.py +27 -0
  42. amapy-utils-1.0.0/asset_utils/utils/tests/test_progress.py +57 -0
  43. amapy-utils-1.0.0/asset_utils/utils/tests/test_utils.py +76 -0
  44. amapy-utils-1.0.0/asset_utils/utils/utils.py +380 -0
  45. amapy-utils-1.0.0/asset_utils/utils/web_utils.py +26 -0
  46. amapy-utils-1.0.0/pyproject.toml +40 -0
  47. 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,2 @@
1
+ [console_scripts]
2
+ utils = asset_utils.utils.pager:run
@@ -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,7 @@
1
+ from .better_set import BetterSet
2
+ from .class_property import classproperty
3
+ from .singleton import Singleton
4
+
5
+ DEBUG = True
6
+ PROFILE = True
7
+ PRINT_ARGS = False
@@ -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