diff --git a/collections.py b/collections.py index 091a1d4ce145b7bd2a614bfcd65eabb2711229d0..c3b676d7802ff3bc871875b27586f4425dbad3ee 100644 --- a/collections.py +++ b/collections.py @@ -16,7 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with Ammsml. If not, see . -from collections.abc import Sequence + +from collections.abc import Sequence, Mapping, Hashable def is_string(seq): @@ -55,11 +56,14 @@ def count(seq): Resembles the `collections.Counter` class functionality, which is deprecated since version 3.3, will be removed in version 3.9 - It is meant to be replaced when the we find something equivalent in maybe collections.abc + It is meant to be replaced when we find something equivalent in maybe collections.abc this code also runs on Python 2.6.* where collections.Counter is not available. + :param seq: + :return: """ + if not is_iterable(seq): raise Exception('Argument provided is not an iterable') @@ -69,3 +73,49 @@ def count(seq): for elem in seq: counters[elem] = counters.get(elem, 0) + 1 return counters + + +class ImmutableDict(Hashable, Mapping): + """ + Dictionary that cannot be updated + """ + def __init__(self, *args, **kwargs): + self._store = dict(*args, **kwargs) + + def __getitem__(self, key): + return self._store[key] + + def __iter__(self): + return self._store.__iter__() + + def __len__(self): + return self._store.__len__() + + def __hash__(self): + return hash(frozenset(self.items())) + + def __repr__(self): + return 'ImmutableDict({0})'.format(repr(self._store)) + + def union(self, overriding_mapping): + """ + Create an ImmutableDict as a combination of the original and overriding_mapping + + :param overriding_mapping: A Mapping of replacement and additional items + :return: A copy of the ImmutableDict with key-value pairs from the overriding_mapping added + + If any of the keys in overriding_mapping are already present in the original ImmutableDict, + the overriding_mapping item replaces the one in the original ImmutableDict. + """ + return ImmutableDict(self._store, **overriding_mapping) + + def difference(self, subtractive_iterable): + """ + Create an ImmutableDict as a combination of the original minus keys in subtractive_iterable + + :param subtractive_iterable: Any iterable containing keys that should not be present in the new ImmutableDict + :return: A copy of the ImmutableDict with keys from the subtractive_iterable removed + """ + remove_keys = frozenset(subtractive_iterable) + keys = (k for k in self._store.keys() if k not in remove_keys) + return ImmutableDict((k, self._store[k]) for k in keys) diff --git a/parsing/__init__.py b/parsing/__init__.py index cdfd95fd521e80aa317da46d9bb7eacbe5b80db1..1a1e5dfd6fd77b4cc4fa3eb0dae9ad088c2328dc 100644 --- a/parsing/__init__.py +++ b/parsing/__init__.py @@ -79,6 +79,20 @@ def is_quoted(data): return len(data) > 1 and data[0] == data[-1] and data[0] in ('"', "'") and data[-2] != '\\' - +def humanize_time(d) -> str: + """ + Return a human-friendly description of elapsed time + :param d: + :return: + """ + human = "" + if d >= 3600: + human += "%dh" % (int(d) // 3600) + d %= 3600 + if d >= 60: + human += "%dm" % (int(d) // 60) + d %= 60 + human += ("%.3fs" % d) + return human diff --git a/singleton.py b/singleton.py index ce9f97f26e56140c226f3f4db00feceaf096dfb8..bb4b69ec04612a7104b04833e435e133a0e9df8e 100644 --- a/singleton.py +++ b/singleton.py @@ -57,3 +57,18 @@ def with_metaclass(meta, *bases): def __prepare__(cls, name, this_bases): return meta.__prepare__(name, bases) return type.__new__(metaclass, 'temporary_class', (), {}) + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper \ No newline at end of file diff --git a/tests/test_parsing/test_humanize_time.py b/tests/test_parsing/test_humanize_time.py new file mode 100644 index 0000000000000000000000000000000000000000..4b9cbea7e72325a4dd20bae4372ee646fa4a9818 --- /dev/null +++ b/tests/test_parsing/test_humanize_time.py @@ -0,0 +1,11 @@ +import unittest +from ammsml.utils.parsing import humanize_time + + +class Test_humanize_time(unittest.TestCase): + def test_something(self): + self.assertEqual(True, False) + # TODO + +if __name__ == '__main__': + unittest.main()