diff --git a/CHANGELOG.md b/CHANGELOG.md index a9f8f92ce9b7b108807fcedbab4ad15236b9bb2d..b46c253fb88455a19424af3b7ef448f08dca2263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,3 @@ -# v2.16.2 +# v2.16.3 -* added optional argument to `File.get_value` -* added `file_contents` schema -* added nested configuration sources -* added `extract_optional` -* added some syntactic sugar for the source dict -* now unparseable "type" entries will be returned as-is -* added `ConfigurationMisconfiguredError` +* added `ComparableAndHashableByStr` diff --git a/satella/__init__.py b/satella/__init__.py index b34264c0170bdade762aeb84e205670aa068b860..9157e8053c94c5086da050d9d01f7b7d18c56a9d 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.16.2' +__version__ = '2.16.3' diff --git a/satella/coding/structures/__init__.py b/satella/coding/structures/__init__.py index e0efee2bedfa722402e4b9061e84ebb95675d5cb..95932389fdb433567659ed35da2ee0af9388b8ac 100644 --- a/satella/coding/structures/__init__.py +++ b/satella/coding/structures/__init__.py @@ -6,7 +6,7 @@ from .heaps import Heap, SetHeap, TimeBasedHeap, TimeBasedSetHeap from .immutable import Immutable, frozendict from .mixins import OmniHashableMixin, ReprableMixin, StrEqHashableMixin, ComparableIntEnum, \ HashableIntEnum, ComparableAndHashableBy, ComparableAndHashableByInt, ComparableEnum, \ - HashableMixin + HashableMixin, ComparableAndHashableByStr from .proxy import Proxy from .queues import Subqueue from .ranking import Ranking @@ -41,6 +41,7 @@ __all__ = [ 'ComparableIntEnum', 'ComparableAndHashableBy', 'HashableIntEnum', + 'ComparableAndHashableByStr', 'DirtyDict', 'SortedList', 'SelfCleaningDefaultDict', diff --git a/satella/coding/structures/mixins/__init__.py b/satella/coding/structures/mixins/__init__.py index 4f300d6db764b442d63f59b8cca35b1c732895e9..85e9f77bb2d947421f53b62a022f34c577c52fa0 100644 --- a/satella/coding/structures/mixins/__init__.py +++ b/satella/coding/structures/mixins/__init__.py @@ -1,8 +1,9 @@ from .enums import ComparableEnum, ComparableIntEnum, HashableIntEnum from .hashable import ComparableAndHashableBy, ComparableAndHashableByInt, \ - OmniHashableMixin, HashableMixin + OmniHashableMixin, HashableMixin, ComparableAndHashableByStr from .strings import ReprableMixin, StrEqHashableMixin __all__ = ['ComparableIntEnum', 'ComparableEnum', 'ComparableAndHashableBy', 'HashableIntEnum', 'ComparableAndHashableByInt', 'OmniHashableMixin', - 'ReprableMixin', 'StrEqHashableMixin', 'HashableMixin'] + 'ReprableMixin', 'StrEqHashableMixin', 'HashableMixin', + 'ComparableAndHashableByStr'] diff --git a/satella/coding/structures/mixins/hashable.py b/satella/coding/structures/mixins/hashable.py index aac2d882459fe16b2d1d8bbbe1c4048a63c9a07d..930ca386cd1319a136686150a8777eb0dcf824c2 100644 --- a/satella/coding/structures/mixins/hashable.py +++ b/satella/coding/structures/mixins/hashable.py @@ -97,6 +97,37 @@ class ComparableAndHashableByInt(metaclass=ABCMeta): return int(self) >= int(other) +class ComparableAndHashableByStr(metaclass=ABCMeta): + """ + A mix-in. Provides comparision (lt, gt, ge, le, eq) and hashing by __str__ of this class. + Also, will make this class equal to strings that return the same value. + """ + + __slots__ = () + + @abstractmethod + def __str__(self) -> str: + ... + + def __hash__(self): + return hash(str(self)) + + def __eq__(self, other: 'ComparableAndHashableByInt') -> bool: + return str(self) == str(other) + + def __lt__(self, other: 'ComparableAndHashableByInt') -> bool: + return str(self) < str(other) + + def __le__(self, other: 'ComparableAndHashableByInt') -> bool: + return str(self) <= str(other) + + def __gt__(self, other: 'ComparableAndHashableByInt') -> bool: + return str(self) > str(other) + + def __ge__(self, other: 'ComparableAndHashableByInt') -> bool: + return str(self) >= str(other) + + class OmniHashableMixin(metaclass=ABCMeta): """ A mix-in. Provides hashing and equal comparison for your own class using specified fields. diff --git a/tests/test_coding/test_structures.py b/tests/test_coding/test_structures.py index 9d7a7605b8b5fcb1f1aa1a6f40d068b8ee031b4a..82e210d3aa97ac6fbbf0eda0a57f459ebc3eba7d 100644 --- a/tests/test_coding/test_structures.py +++ b/tests/test_coding/test_structures.py @@ -14,7 +14,8 @@ from satella.coding.structures import TimeBasedHeap, Heap, typednamedtuple, \ DirtyDict, KeyAwareDefaultDict, Proxy, ReprableMixin, TimeBasedSetHeap, ExpiringEntryDict, SelfCleaningDefaultDict, \ CacheDict, StrEqHashableMixin, ComparableIntEnum, HashableIntEnum, ComparableAndHashableBy, \ ComparableAndHashableByInt, SparseMatrix, ExclusiveWritebackCache, Subqueue, \ - CountingDict, ComparableEnum, LRU, LRUCacheDict, Vector, DefaultDict, PushIterable + CountingDict, ComparableEnum, LRU, LRUCacheDict, Vector, DefaultDict, PushIterable, \ + ComparableAndHashableByStr class TestMisc(unittest.TestCase): @@ -219,6 +220,22 @@ class TestMisc(unittest.TestCase): self.assertFalse(Vector(1) == Vector(2)) self.assertEqual(hash(Vector(1)), hash(1)) + def test_comparable_and_hashable_by_str(self): + class Vector(ComparableAndHashableByStr): + + def __str__(self) -> str: + return self.a + + def __init__(self, a): + self.a = a + + self.assertTrue(Vector('a') < Vector('ab')) + self.assertTrue(Vector('a') <= Vector('a')) + self.assertFalse(Vector('a') > Vector('b')) + self.assertFalse(Vector('a') >= Vector('b')) + self.assertFalse(Vector('a') == Vector('b')) + self.assertEqual(hash(Vector('a')), hash('a')) + def test_hashable_int_enum(self): class A(HashableIntEnum): A = 0