summaryrefslogtreecommitdiff
path: root/game/python-extra/utils/dicts
diff options
context:
space:
mode:
authorJesusaves <cpntb1@ymail.com>2020-12-09 13:32:01 -0300
committerJesusaves <cpntb1@ymail.com>2020-12-09 13:32:01 -0300
commit63afe4145f410a844c647d4e3f1059f568175c1e (patch)
tree15da6a890c78d73370f44f9fd5d59badfbbe60e4 /game/python-extra/utils/dicts
downloadclient-init.tar.gz
client-init.tar.bz2
client-init.tar.xz
client-init.zip
Initial commit, forked from Spheresinit
Diffstat (limited to 'game/python-extra/utils/dicts')
-rw-r--r--game/python-extra/utils/dicts/__init__.py7
-rw-r--r--game/python-extra/utils/dicts/chained_dict.py71
-rw-r--r--game/python-extra/utils/dicts/helpers.py99
-rw-r--r--game/python-extra/utils/dicts/limited_dict.py40
4 files changed, 217 insertions, 0 deletions
diff --git a/game/python-extra/utils/dicts/__init__.py b/game/python-extra/utils/dicts/__init__.py
new file mode 100644
index 0000000..0ab7c69
--- /dev/null
+++ b/game/python-extra/utils/dicts/__init__.py
@@ -0,0 +1,7 @@
+"""Helper functinos for dealing with dicts.
+
+Things you always wished you could do more succinctly!
+"""
+from .limited_dict import LimitedDict
+from .chained_dict import ChainedDict
+from .helpers import *
diff --git a/game/python-extra/utils/dicts/chained_dict.py b/game/python-extra/utils/dicts/chained_dict.py
new file mode 100644
index 0000000..f1fe36f
--- /dev/null
+++ b/game/python-extra/utils/dicts/chained_dict.py
@@ -0,0 +1,71 @@
+from collections import MutableMapping
+from itertools import chain
+
+
+class ChainedDict(MutableMapping):
+
+ def __init__(self, parent=None, **kwargs):
+ self.__parent = parent
+ self.__deleted_keys = set()
+ self.__data = kwargs
+
+ def __contains__(self, key):
+ if self.__parent is not None:
+ return (
+ (key in self.__data or key in self.__parent)
+ and key not in self.__deleted_keys
+ )
+ return key in self.__data
+
+ def __getitem__(self, key):
+ try:
+ return self.__data[key]
+ except KeyError:
+ if self.__parent is not None and key not in self.__deleted_keys:
+ return self.__parent[key]
+ else:
+ raise
+
+ def __setitem__(self, key, val):
+ self.__data[key] = val
+ self.__deleted_keys.discard(key)
+
+ def __delitem__(self, key):
+ if key in self:
+ self.__deleted_keys.add(key)
+ try:
+ del self.__data[key]
+ except KeyError:
+ pass
+ else:
+ raise KeyError(key)
+
+ def __repr__(self):
+ return "{}({})".format(self.__class__.__name__, dict(self.items()))
+
+ def __iter__(self):
+ return self.keys()
+
+ def __len__(self):
+ return len(list(self.keys()))
+
+ def iterkeys(self):
+ yielded = set(self.__deleted_keys)
+ if self.__parent is None:
+ iterable = self.__data.keys()
+ else:
+ iterable = chain(self.__parent.keys(), self.__data.keys())
+
+ for key in iterable:
+ if key in yielded:
+ continue
+ yield key
+ yielded.add(key)
+
+ keys = iterkeys
+
+ def iteritems(self):
+ for key in self.iterkeys():
+ yield key, self[key]
+
+ items = iteritems
diff --git a/game/python-extra/utils/dicts/helpers.py b/game/python-extra/utils/dicts/helpers.py
new file mode 100644
index 0000000..8b1f594
--- /dev/null
+++ b/game/python-extra/utils/dicts/helpers.py
@@ -0,0 +1,99 @@
+from collections import namedtuple
+
+
+def from_keyed_iterable(iterable, key, filter_func=None):
+ """Construct a dictionary out of an iterable, using an attribute name as
+ the key. Optionally provide a filter function, to determine what should be
+ kept in the dictionary."""
+
+ generated = {}
+
+ for element in iterable:
+ try:
+ k = getattr(element, key)
+ except AttributeError:
+ raise RuntimeError("{} does not have the keyed attribute: {}".format(
+ element, key
+ ))
+
+ if filter_func is None or filter_func(element):
+ if k in generated:
+ generated[k] += [element]
+ else:
+ generated[k] = [element]
+
+ return generated
+
+
+def subtract_by_key(dict_a, dict_b):
+ """given two dicts, a and b, this function returns c = a - b, where
+ a - b is defined as the key difference between a and b.
+
+ e.g.,
+ {1:None, 2:3, 3:"yellow", 4:True} - {2:4, 1:"green"} =
+ {3:"yellow", 4:True}
+
+ """
+ difference_dict = {}
+ for key in dict_a:
+ if key not in dict_b:
+ difference_dict[key] = dict_a[key]
+
+ return difference_dict
+
+
+def subtract(dict_a, dict_b, strict=False):
+ """a stricter form of subtract_by_key(), this version will only remove an
+ entry from dict_a if the key is in dict_b *and* the value at that key
+ matches"""
+ if not strict:
+ return subtract_by_key(dict_a, dict_b)
+
+ difference_dict = {}
+ for key in dict_a:
+ if key not in dict_b or dict_b[key] != dict_a[key]:
+ difference_dict[key] = dict_a[key]
+
+ return difference_dict
+
+
+WinnowedResult = namedtuple("WinnowedResult", ['has', 'has_not'])
+def winnow_by_keys(dct, keys=None, filter_func=None):
+ """separates a dict into has-keys and not-has-keys pairs, using either
+ a list of keys or a filtering function."""
+ has = {}
+ has_not = {}
+
+ for key in dct:
+ key_passes_check = False
+ if keys is not None:
+ key_passes_check = key in keys
+ elif filter_func is not None:
+ key_passes_check = filter_func(key)
+
+ if key_passes_check:
+ has[key] = dct[key]
+ else:
+ has_not[key] = dct[key]
+
+ return WinnowedResult(has, has_not)
+
+
+def intersection(dict_a, dict_b, strict=True):
+ intersection_dict = {}
+
+ for key in dict_a:
+ if key in dict_b:
+ if not strict or dict_a[key] == dict_b[key]:
+ intersection_dict[key] = dict_a[key]
+
+ return intersection_dict
+
+
+def setdefaults(dct, defaults):
+ """Given a target dct and a dict of {key:default value} pairs,
+ calls setdefault for all of those pairs."""
+ for key in defaults:
+ dct.setdefault(key, defaults[key])
+
+ return dct
diff --git a/game/python-extra/utils/dicts/limited_dict.py b/game/python-extra/utils/dicts/limited_dict.py
new file mode 100644
index 0000000..c690118
--- /dev/null
+++ b/game/python-extra/utils/dicts/limited_dict.py
@@ -0,0 +1,40 @@
+from collections import MutableMapping
+
+
+class LimitedDict(MutableMapping):
+ def __init__(self, args=None, **kwargs):
+ keys = kwargs.pop('keys', [])
+ self.__keys = keys
+
+ self.__data = {}
+
+ if args:
+ kwargs.update((key, val) for key, val in args)
+
+ for key, val in kwargs.items():
+ self[key] = val
+
+ def __setitem__(self, key, val):
+ if key not in self.__keys:
+ raise KeyError("Illegal key: {}".format(key))
+
+ self.__data[key] = val
+
+ def __getitem__(self, key):
+ return self.__data[key]
+
+ def __iter__(self):
+ return self.__data.__iter__()
+
+ def __delitem__(self, key):
+ del self.__data[key]
+
+ def __len__(self):
+ return len(self.__data)
+
+ def __repr__(self):
+ return "{}({}, {})".format(self.__class__.__name__, self.defined_keys, self.__data)
+
+ @property
+ def defined_keys(self):
+ return self.__keys