Skip to content
Snippets Groups Projects
Commit 62811eee authored by Piotr Maślanka's avatar Piotr Maślanka
Browse files

add `ComparableAndHashableBy`

parent c9257ac9
No related branches found
Tags v2.9.3
No related merge requests found
# v2.9.3
* `TerminableThread.start` now returns self
* add `ComparableAndHashableBy`
......@@ -58,6 +58,12 @@ If you need quick __hash__ and __eq__ operators from listed fields of the class.
.. autoclass:: satella.coding.structures.OmniHashableMixin
:members:
ComparableAndHashableBy
-----------------------
.. autoclass:: satella.coding.structures.ComparableAndHashableBy
:members:
StrEqHashableMixin
------------------
......
__version__ = '2.9.3_a2'
__version__ = '2.9.3'
......@@ -5,7 +5,7 @@ from .hashable_objects import HashableWrapper
from .heaps import Heap, SetHeap, TimeBasedHeap, TimeBasedSetHeap
from .immutable import Immutable, frozendict
from .mixins import OmniHashableMixin, ReprableMixin, StrEqHashableMixin, ComparableIntEnum, \
HashableIntEnum
HashableIntEnum, ComparableAndHashableBy
from .proxy import Proxy
from .ranking import Ranking
from .singleton import Singleton, SingletonWithRegardsTo
......@@ -19,6 +19,7 @@ __all__ = [
'ReprableMixin',
'StrEqHashableMixin',
'ComparableIntEnum',
'ComparableAndHashableBy',
'HashableIntEnum',
'DirtyDict',
'SortedList',
......
......@@ -58,6 +58,48 @@ class StrEqHashableMixin(metaclass=ABCMeta):
pass
class ComparableAndHashableBy(metaclass=ABCMeta):
"""
A mix-in. Provides comparision (lt, gt, ge, le, eq) and hashing by a field of this class.
Example:
>>> class Vector(ComparableAndHashableBy):
>>> _COMPARABLE_BY = 'length'
>>> @property
>>> def length(self):
>>> ...
>>>
>>> assert Vector() > Vector()
"""
@property
@abstractmethod
def _COMPARABLE_BY(self) -> str:
"""
Return the sequence of names of properties and attributes
that will be used for __eq__ and __hash__
"""
return ''
def __hash__(self):
return hash(getattr(self, self._COMPARABLE_BY))
def __eq__(self, other: 'ComparableAndHashableBy') -> bool:
return getattr(self, self._COMPARABLE_BY) == getattr(other, other._COMPARABLE_BY)
def __lt__(self, other: 'ComparableAndHashableBy') -> bool:
return getattr(self, self._COMPARABLE_BY) < getattr(other, other._COMPARABLE_BY)
def __le__(self, other: 'ComparableAndHashableBy') -> bool:
return getattr(self, self._COMPARABLE_BY) <= getattr(other, other._COMPARABLE_BY)
def __gt__(self, other: 'ComparableAndHashableBy') -> bool:
return getattr(self, self._COMPARABLE_BY) > getattr(other, other._COMPARABLE_BY)
def __ge__(self, other: 'ComparableAndHashableBy') -> bool:
return getattr(self, self._COMPARABLE_BY) >= getattr(other, other._COMPARABLE_BY)
class OmniHashableMixin(metaclass=ABCMeta):
"""
A mix-in. Provides hashing and equal comparison for your own class using specified fields.
......
......@@ -11,11 +11,25 @@ from satella.coding.structures import TimeBasedHeap, Heap, typednamedtuple, \
OmniHashableMixin, DictObject, apply_dict_object, Immutable, frozendict, SetHeap, \
DictionaryView, HashableWrapper, TwoWayDictionary, Ranking, SortedList, SliceableDeque, \
DirtyDict, KeyAwareDefaultDict, Proxy, ReprableMixin, TimeBasedSetHeap, ExpiringEntryDict, SelfCleaningDefaultDict, \
CacheDict, StrEqHashableMixin, ComparableIntEnum, HashableIntEnum
CacheDict, StrEqHashableMixin, ComparableIntEnum, HashableIntEnum, ComparableAndHashableBy
class TestMisc(unittest.TestCase):
def test_comparable_and_hashable_by(self):
class Vector(ComparableAndHashableBy):
_COMPARABLE_BY = 'a'
def __init__(self, a):
self.a = a
self.assertTrue(Vector(1) < Vector(2))
self.assertTrue(Vector(1) <= Vector(2))
self.assertFalse(Vector(1) > Vector(2))
self.assertFalse(Vector(1) >= Vector(2))
self.assertFalse(Vector(1) == Vector(2))
self.assertEqual(hash(Vector(1)), hash(1))
def test_hashable_int_enum(self):
class A(HashableIntEnum):
A = 0
......
......@@ -13,6 +13,12 @@ class TestMetricData(unittest.TestCase):
md2 = MetricData.from_json(md.to_json())
self.assertTrue(md2.internal)
def test_metric_data_collection_add(self):
a = MetricDataCollection(MetricData('root', 3, {'labels': 'key'}),
MetricData('root_a', 3, {'labels': 'key'}))
a += MetricDataCollection(MetricData('root', 2, {'labels': 'key'}),
MetricData('root_a', 4, {'labels': 'key'}))
def test_update_labels_2(self):
a = MetricDataCollection(MetricData('root', 2, {'labels': 'key'}))
a.add_labels({'service': 'wtf'})
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment