107 lines
3.2 KiB
Python
107 lines
3.2 KiB
Python
import urllib.parse
|
|
|
|
STRICT_VALIDATION = True
|
|
|
|
|
|
def canonical_keys(base_key, context):
|
|
if isinstance(base_key, urllib.parse.ParseResult):
|
|
return [base_key]
|
|
if not isinstance(base_key, str):
|
|
return [base_key]
|
|
elif base_key.startswith('@'):
|
|
return [base_key]
|
|
if context is None:
|
|
return [base_key]
|
|
return [context._replace(path=base_key), base_key]
|
|
|
|
|
|
class Concept:
|
|
def __init__(self, context, pairs):
|
|
self.pairs = []
|
|
for k, v in pairs.items():
|
|
keys = canonical_keys(k, context)
|
|
self.pairs.append(
|
|
{'canonical_key': keys[0], 'keys': set(keys), 'values': v}
|
|
)
|
|
self.regenerate_by_keys()
|
|
|
|
def regenerate_by_keys(self):
|
|
self.by_keys = {k: pair for pair in self.pairs for k in pair['keys']}
|
|
|
|
def __copy__(self):
|
|
new = Concept(None, {})
|
|
for p in self.pairs:
|
|
new.pairs.append(
|
|
{
|
|
'canonical_key': p['canonical_key'],
|
|
'keys': set(p['keys']),
|
|
'values': p['values'],
|
|
}
|
|
)
|
|
new.regenerate_by_keys()
|
|
return new
|
|
|
|
def get(self, key, default=None):
|
|
pairs = self.by_keys.get(key, None)
|
|
return pairs['values'] if pairs is not None else default
|
|
|
|
def getlist(self, key):
|
|
result = self.get(key)
|
|
if result is None:
|
|
return []
|
|
assert isinstance(result, list), 'Not a list: ' + str(result)
|
|
return [r['value'] for r in result]
|
|
|
|
def keys(self):
|
|
for pair in self.pairs:
|
|
yield pair['canonical_key']
|
|
|
|
def setdefault(self, key, value):
|
|
if key not in self.by_keys:
|
|
self[key] = value
|
|
return self.by_keys[key]['values']
|
|
|
|
def to_dict(self):
|
|
return {p['canonical_key']: p['values'] for p in self.pairs}
|
|
|
|
def __getitem__(self, key):
|
|
return self.by_keys[key]['values']
|
|
|
|
def __setitem__(self, key, value):
|
|
if STRICT_VALIDATION:
|
|
if not isinstance(key, str) or key != '@id':
|
|
assert isinstance(value, list), value
|
|
for v in value:
|
|
assert isinstance(v, dict), value
|
|
assert 'value' in v, value
|
|
for subk in v:
|
|
assert not isinstance(v[subk], list), value
|
|
|
|
if key in self.by_keys:
|
|
self.by_keys[key]['values'] = value
|
|
else:
|
|
pair = {'canonical_key': key, 'keys': {key}, 'values': value}
|
|
self.pairs.append(pair)
|
|
self.by_keys[key] = pair
|
|
|
|
def __contains__(self, key):
|
|
return key in self.by_keys
|
|
|
|
def __delitem__(self, key):
|
|
self.pairs.remove(self.by_keys[key])
|
|
del self.by_keys[key]
|
|
|
|
def __repr__(self):
|
|
if id := self.by_keys.get('@id'):
|
|
return 'Concept {{ @id = {} }}'.format(id['values'])
|
|
|
|
return 'Concept ' + str({p['canonical_key']: p['values'] for p in self.pairs})
|
|
|
|
def __str__(self):
|
|
return repr(self)
|
|
|
|
def set_canonical_key(self, new_canonical_key, key=None):
|
|
if key is None:
|
|
key = new_canonical_key
|
|
self.by_keys[key]['canonical_key'] = new_canonical_key
|