X-Git-Url: http://dolda2000.com/gitweb/?p=didex.git;a=blobdiff_plain;f=didex%2Fvalues.py;h=ecb78dd26d1cd70ce5f1380279e7ab2d7613743e;hp=090edd4c86d77bd46877c49cde00d0538f43ce25;hb=874b91d5878b929b53a227a3a409d8c507eb093c;hpb=cbf73d3a70b97f17f1f5431eee1e73dbc56a7f8e diff --git a/didex/values.py b/didex/values.py index 090edd4..ecb78dd 100644 --- a/didex/values.py +++ b/didex/values.py @@ -1,8 +1,8 @@ import threading -from . import store, lib +from . import store, lib, index from .store import storedesc -__all__ = ["simple", "multi"] +__all__ = ["simple", "multi", "compound"] class cursor(lib.closable): def __init__(self, bk, st): @@ -23,16 +23,13 @@ class cursor(lib.closable): self.bk.skip(n) class base(storedesc): - def __init__(self, store, indextype, name, datatype, default): + def __init__(self, store, indextype, name, datatype): self.store = store self.indextype = indextype self.name = name self.typ = datatype - self.default = default self.idx = None self.lk = threading.Lock() - self.mattr = "__idx_%s_new" % name - self.iattr = "__idx_%s_cur" % name def index(self): with self.lk: @@ -40,6 +37,37 @@ class base(storedesc): self.idx = self.indextype(self.store.db(), self.name, self.typ) return self.idx + def get(self, **kwargs): + return cursor(self.index().get(**kwargs), self.store) + + def get1(self, *, check=False, default=KeyError, **kwargs): + with self.get(**kwargs) as cursor: + try: + k, v = next(cursor) + except StopIteration: + if default is not KeyError: + return default + raise KeyError("no matches in " + self.name, kwargs) + if check: + try: + next(cursor) + except StopIteration: + pass + else: + raise ValueError("unexpected multiple matchies in " + self.name, kwargs) + return v + + def list(self, **kwargs): + with self.get(**kwargs) as cursor: + return [v for k, v in cursor] + +class descbase(base): + def __init__(self, store, indextype, name, datatype, default): + super().__init__(store, indextype, name, datatype) + self.default = default + self.mattr = "__idx_%s_new" % name + self.iattr = "__idx_%s_cur" % name + def __get__(self, obj, cls): if obj is None: return self return getattr(obj, self.mattr, self.default) @@ -50,10 +78,7 @@ class base(storedesc): def __delete__(self, obj): delattr(obj, self.mattr) - def get(self, **kwargs): - return cursor(self.index().get(**kwargs), self.store) - -class simple(base): +class simple(descbase): def __init__(self, store, indextype, name, datatype, default=None): super().__init__(store, indextype, name, datatype, default) @@ -75,7 +100,7 @@ class simple(base): idx.put(val, id, tx=tx) tx.postcommit(lambda: setattr(obj, self.iattr, val)) -class multi(base): +class multi(descbase): def __init__(self, store, indextype, name, datatype): super().__init__(store, indextype, name, datatype, ()) @@ -102,3 +127,38 @@ class multi(base): for val in vals - ivals: idx.put(val, id, tx=tx) tx.postcommit(lambda: setattr(obj, self.iattr, vals)) + +class compound(base): + def __init__(self, indextype, name, *parts): + super().__init__(parts[0].store, indextype, name, index.compound(*(part.typ for part in parts))) + self.parts = parts + self.iattr = "__idx_%s_cur" % name + + def minim(self, *parts): + return self.typ.minim(*parts) + def maxim(self, *parts): + return self.typ.maxim(*parts) + + def get(self, *, partial=None, **spec): + if partial is not None: + return super().get(ge=self.minim(*partial), le = self.maxim(*partial), **spec) + else: + return super().get(**spec) + + def register(self, id, obj, tx): + val = tuple(part.__get__(obj, None) for part in self.parts) + self.index().put(val, id, tx=tx) + tx.postcommit(lambda: setattr(obj, self.iattr, val)) + + def unregister(self, id, obj, tx): + self.index().remove(getattr(obj, self.iattr), id, tx=tx) + tx.postcommit(lambda: delattr(obj, self.iattr)) + + def update(self, id, obj, tx): + val = tuple(part.__get__(obj, None) for part in self.parts) + ival = getattr(obj, self.iattr) + if val != ival: + idx = self.index() + idx.remove(ival, id, tx=tx) + idx.put(val, id, tx=tx) + tx.postcommit(lambda: setattr(obj, self.iattr, val))