X-Git-Url: http://dolda2000.com/gitweb/?p=didex.git;a=blobdiff_plain;f=didex%2Fvalues.py;h=debd29e07a89f7cf1ae5fb0b7708deb6281d35b1;hp=0e7bddc13041e68add450428bfed3b8713cfd5c2;hb=cbcc163b2acbcf15e1819d73cf9625c5fadfadf8;hpb=b080a59cbbe6b293d754bfa97c386eeede6b51ee diff --git a/didex/values.py b/didex/values.py index 0e7bddc..debd29e 100644 --- a/didex/values.py +++ b/didex/values.py @@ -1,7 +1,9 @@ import threading -from . import store, lib +from . import store, lib, index from .store import storedesc +__all__ = ["simple", "multi", "compound", "idlink"] + class cursor(lib.closable): def __init__(self, bk, st): self.bk = bk @@ -21,23 +23,50 @@ 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): + def index(self, tx): with self.lk: if self.idx is None: - self.idx = self.indextype(self.store.db(), self.name, self.typ) + self.idx = self.indextype(self.store.db(), self.name, self.typ, tx=tx) return self.idx + def get(self, **kwargs): + return cursor(self.index(None).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 = "__ival_%s" % name + def __get__(self, obj, cls): if obj is None: return self return getattr(obj, self.mattr, self.default) @@ -48,55 +77,116 @@ 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) def register(self, id, obj, tx): val = self.__get__(obj, None) - self.index().put(val, id, tx=tx) - tx.postcommit(lambda: setattr(obj, self.iattr, val)) + self.index(tx).put(val, id, tx=tx) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), val)) def unregister(self, id, obj, tx): - self.index().remove(getattr(obj, self.iattr), id, tx=tx) - tx.postcommit(lambda: delattr(obj, self.iattr)) + self.index(tx).remove(self.store.icache[obj, self], id, tx=tx) + tx.postcommit(lambda: self.store.icache.__delitem__((obj, self))) def update(self, id, obj, tx): val = self.__get__(obj, None) - ival = getattr(obj, self.iattr) + ival = self.store.icache[obj, self] if val != ival: - idx = self.index() + idx = self.index(tx) idx.remove(ival, id, tx=tx) idx.put(val, id, tx=tx) - tx.postcommit(lambda: setattr(obj, self.iattr, val)) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), val)) + + def loaded(self, id, obj, tx): + val = self.__get__(obj, None) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), val)) -class multi(base): +class multi(descbase): def __init__(self, store, indextype, name, datatype): super().__init__(store, indextype, name, datatype, ()) def register(self, id, obj, tx): vals = frozenset(self.__get__(obj, None)) - idx = self.index() + idx = self.index(tx) for val in vals: idx.put(val, id, tx=tx) - tx.postcommit(lambda: setattr(obj, self.iattr, vals)) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), vals)) def unregister(self, id, obj, tx): - idx = self.index() - for val in getattr(obj, self.iattr): + idx = self.index(tx) + for val in self.store.icache[obj, self]: idx.remove(val, id, tx=tx) - tx.postcommit(lambda: delattr(obj, self.iattr)) + tx.postcommit(lambda: self.store.icache.__delitem__((obj, self))) def update(self, id, obj, tx): vals = frozenset(self.__get__(obj, None)) - ivals = getattr(obj, self.iattr) + ivals = self.store.icache[obj, self] if vals != ivals: - idx = self.index() + idx = self.index(tx) for val in ivals - vals: idx.remove(val, id, tx=tx) for val in vals - ivals: idx.put(val, id, tx=tx) - tx.postcommit(lambda: setattr(obj, self.iattr, vals)) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), val)) + + def loaded(self, id, obj, tx): + vals = frozenset(self.__get__(obj, None)) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), 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 + + 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(tx).put(val, id, tx=tx) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), val)) + + def unregister(self, id, obj, tx): + self.index(tx).remove(self.store.icache[obj, self], id, tx=tx) + tx.postcommit(lambda: self.store.icache.__delitem__((obj, self))) + + def update(self, id, obj, tx): + val = tuple(part.__get__(obj, None) for part in self.parts) + ival = self.store.icache[obj, self] + if val != ival: + idx = self.index(tx) + idx.remove(ival, id, tx=tx) + idx.put(val, id, tx=tx) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), val)) + + def loaded(self, id, obj, tx): + val = tuple(part.__get__(obj, None) for part in self.parts) + tx.postcommit(lambda: self.store.icache.__setitem__((obj, self), val)) + +class idlink(object): + def __init__(self, name, atype): + self.atype = atype + self.battr = "__idlink_%s" % name + + def __get__(self, obj, cls): + if obj is None: return self + ret = self.atype.store.get(getattr(obj, self.battr)) + assert isinstance(ret, self.atype) + return ret + + def __set__(self, obj, val): + assert isinstance(val, self.atype) + setattr(obj, self.battr, val.id) + + def __delete__(self, obj): + delattr(obj, self.battr)