-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
util.py
160 lines (124 loc) · 4.46 KB
/
util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
"""
Utility functionality that isn't specific to a given module
"""
import flask_caching
import functools
from lidarrmetadata import config
# Cache for application
CACHE = flask_caching.Cache(config=config.get_config().CACHE_CONFIG)
def cache_or_call(func, *args, **kwargs):
"""
Gets cache result or calls function with args and kwargs
:param func: Function to call
:param args: Args to call func with
:param kwargs: Kwargs to call func with
:return: Result of func(*args, **kwargs)
"""
# This may not work well if args or kwargs contain objects, but we don't need to handle that at the moment
key = str((function_hash(func), repr(args), repr(kwargs)))
ret = CACHE.get(key)
if not ret:
ret = func(*args, **kwargs)
CACHE.set(key, ret)
return ret
def first_key_item(dictionary, key, default=None):
"""
Gets the first item from a dictionary key that returns a list
:param dictionary: Dictionary to get item from
:param key: Key to get
:param default: Default value to use
:return: First item or default
"""
value = dictionary.get(key, default)
if value and value != default and hasattr(value, '__getitem__'):
return value[0]
return value
def function_hash(func):
"""
Hashes function to determine uniqueness of function. Used for versioning functions in caches
:param func: Function to hash
:return: Hash representing function. Unique for bytecode of function
"""
return hash(func.__code__)
def map_iterable_values(iterable, func, types=object):
"""
Maps all strings in iterable
!!!
This function was a fun one to write. Don't modify until tests are added unless there's an issue.
!!!
:param iterable: Iterable to find strings in
:param func: String mapping function
:param types: Type restriction as single type or iterable of types. Defaults to object (no type restriction)
:return: Iterable with mapped strings
"""
types = (types,) if isinstance(types, type) else tuple(types)
original_type = type(iterable)
if original_type not in [dict, list]:
iterable = list(iterable)
mapped = type(iterable)()
assign_func = mapped.setdefault if isinstance(mapped, dict) else mapped.insert
enumerate_func = iterable.items if isinstance(iterable, dict) else functools.partial(enumerate, iterable)
for i, v in enumerate_func():
if isinstance(v, types):
assign_func(i, func(v))
elif hasattr(v, '__iter__') and not isinstance(v, str):
assign_func(i, map_iterable_values(v, func, types))
else:
assign_func(i, v)
if original_type not in [dict, list]:
mapped = original_type(mapped)
return mapped
def translate_string(s, table):
"""
Translated a string based on translation table
:param s: String to translate
:param table: Tranalation table as dictionary
:return: Translated string
"""
return ''.join([table.get(c, c) for c in s])
class BidirectionalDictionary(dict):
"""
Bidirectional dictionary. Thanks to https://stackoverflow.com/a/21894086/2383721. Modified to only have unique
values
"""
def __init__(self, *args, **kwargs):
super(BidirectionalDictionary, self).__init__(*args, **kwargs)
self.inverse = {}
for key, value in self.items():
self.inverse.setdefault(value, key)
def __setitem__(self, key, value):
if key in self:
del self.inverse[value]
super(BidirectionalDictionary, self).__setitem__(key, value)
self.inverse.setdefault(value, key)
def __delitem__(self, key):
del self.inverse[self[key]]
super(BidirectionalDictionary, self).__delitem__(key)
class Cache(object):
"""
Cache to store info
"""
def __init__(self):
"""
Initialization
"""
self._backend = {}
def __getitem__(self, item):
return self.get(item)
def __setitem__(self, key, value):
return self.put(key, value)
def get(self, key, default=None):
"""
Gets item with key
:param key: Key of item to get
:param default: Default value to return if no item at key
:return: Item at key
"""
return self._backend.get(key, default)
def put(self, key, item):
"""
Puts item at key
:param key: Key to put item at
:param item: Value to put
"""
self._backend[key] = item