Source code for meds.object

# object.py
#
#

"""
    An Object adds 'dotted access' to dicts to allow for better readable code.
    Object's also allow for saving JSON to disk to allow it to be restored at a later time.

"""

from meds.errors import EBORDER, ESET, ENOJSON, ESIGNATURE, ENOTSET, EISMETHOD
from meds.utils.signature import make_signature, verify_signature
from meds.utils.misc import locked, headertxt
from meds.utils.getters import path as dopath
from meds.utils.getters import  urled, slice
from meds.utils.trace import get_strace
from meds.utils.json import dumps
from meds.utils.tijd import rtime
from meds.utils.file import cdir
from meds.utils.join import j
from meds.utils.name import name, naam

import threading
import logging
import inspect
import fcntl
import json
import time
import os

[docs]class Object(dict): """ Core Object of the MEDS bot, provides saving to JSON files, using timestamps in the filename so collections of object in a period of time is possible. constructing an Object: >>> obj = Object() >>> print(obj) {} setting attributes on an Object: >>> obj.key1 = "value1" >>> obj["key2"] = "value2" accessing attributes can also be 'dotted' or with a dict key: >>> v1 = obj.key1 >>> v2 = obj["key2"] saving is easy, the save() method will use the current time as timestamp: >>> path = obj.save() syncing to an already saved file is done with the sync() method: >>> path = obj.sync() the json() method can be used to get a JSON string of the object: >>> s = obj.json() nice() will give you a more readable presentation: >>> s = obj.nice() use the load() method to read a file into an Object: >>> path = obj._path >>> test = Object() >>> obj = test.load(path) """ def __str__(self): return self.nice() def __repr__(self): return '<%s.%s at %s>' % ( self.__class__.__module__, self.__class__.__name__, hex(id(self)) ) def __getattribute__(self, name): if name == "url": return urled(self) try: val = super().__getattribute__(name) except AttributeError: try: val = self[name] except KeyError as ex: raise AttributeError(name) return val def __getattr__(self, name): if name == "_resume": self._resume = Object() if name == "_connected": self._connected = Object() if name == "_funcs": self._funcs = [] if name == "_ready": self._ready = threading.Event() if name == "_result": self._result = [] if name == "_thrs": self._thrs = [] if name not in self: raise AttributeError(name) return self.__getitem__(name) def __setattr__(self, name, value): return self.__setitem__(name, value) def __dir__(self): return self.keys() def __contains__(self, key): if key in self.keys(): return True return False
[docs] def consume(self, value): nr = 0 for x in value: self["key%s" % str(nr)] = x nr += 1
[docs] def grep(self, val): o = Object() for key, value in self.items(): if val in str(value) and value not in o: o[key] = value return o
[docs] def json(self, *args, **kwargs): return dumps(self, *args, **kwargs)
[docs] def load(self, path="", force=True): from meds.core import cfg if not path: path = self._path if cfg.workdir not in path: path = j(cfg.workdir, path) ondisk = self.read(path) fromdisk = json.loads(ondisk) if "signature" in fromdisk: if not verify_signature(fromdisk["data"], fromdisk["signature"]) and not force: raise ESIGNATURE if "data" in fromdisk: self.update(fromdisk["data"]) else: self.update(fromdisk) if "saved" in fromdisk: self._saved = fromdisk["saved"] self._path = path return self
[docs] def merge(self, obj): for key in obj: if key not in self: self[key] = obj[key]
[docs] def nice(self, *args, **kwargs): return dumps(self, indent=4, sort_keys=True)
[docs] def prepare(self): todisk = Object() todisk.data = slice(self) todisk.data.type = self.__class__.__name__ if "saved" not in todisk.data: todisk.data.saved = time.ctime(time.time()) try: todisk.signature = make_signature(todisk["data"]) except: pass try: result = dumps(todisk, indent=4, ensure_ascii=False, sort_keys=True) except TypeError: raise ENOJSON() return result
[docs] def read(self, path): f = open(path, "r", encoding="utf-8") res = "" for line in f: if not line.strip().startswith("#"): res += line if not res.strip(): return "{}" f.close() return res
[docs] def register(self, key, val, force=False): if key in self and not force: raise ESET(key) self[key] = val
[docs] def save(self, stime=""): if not stime: stime = rtime() path = j(dopath(self), stime) return self.sync(path)
[docs] def search(self, name): o = Object() for key, value in self.items(): if key.startswith("_"): continue if key in name: o[key] = value elif name in key.split(".")[-1]: o[key] = value return o
@locked def sync(self, path=""): from meds.core import kernel if not path: try: path = self._path except AttributeError: pass if not path: path = j(dopath(self), rtime()) self._path = os.path.abspath(path) if kernel._cfg.workdir not in self._path: raise EBORDER(self._path) logging.info("! sync %s" % path) d, fn = os.path.split(path) cdir(d) todisk = self.prepare() datafile = open(os.path.abspath(path) + ".tmp", 'w') fcntl.flock(datafile, fcntl.LOCK_EX | fcntl.LOCK_NB) datafile.write(headertxt % "%s characters" % len(todisk)) datafile.write(todisk) datafile.write("\n") fcntl.flock(datafile, fcntl.LOCK_UN) datafile.flush() datafile.close() os.rename(path + ".tmp", path) return path
[docs] def clear(self): self._ready.clear()
[docs] def isSet(self): return self._ready.isSet()
[docs] def ready(self): self._ready.set()
[docs] def wait(self, sec=None): self._ready.wait(sec) for thr in self._thrs: logging.info("! join %s" % naam(thr)) thr.join(1.0)
[docs]class OOL(Object):
[docs] def register(self, key, val): if key not in self: self[key] = [] self[key].append(val)
[docs] def find(self, txt=None): for key, value in self.items(): if txt and txt in key: yield value else: yield value