Source code for core.thing

# core/thing.py
#
#

""" basic package for the program. """

__copyright__ = "Copyright 2015, B.H.J Thate"

## IMPORTS

from core.utils.time import elapsed_days, short_date, rtime, a_time, fn_time, to_day, get_day
from core.utils.file import cdir, check_permissions, list_files
from core.utils.trace import get_plugname, get_location
from core.utils.serialize import smooth, pretty
from core.utils.name import name, typed
from core.utils.parse import txt_parse
from core.utils.other import headertxt
from core.defines import hostname
from core.utils.join import j

import threading
import hashlib
import logging
import errno
import fcntl
import json
import time
import os

## BASE

[docs]class Thing(dict): """ basic Thing on which the rest of the program is based. """ def __getattr__(zelf, name): if name in zelf: return zelf[name] if name == "_ready": zelf._ready = threading.Event() if name == "skip": return ["parsed", ] if name == "url": return zelf.get_url() if name == "timed": return zelf.get_timed() if name == "cc": return "!" if name == "type": return typed(zelf) if name not in zelf: raise AttributeError(name) return zelf[name] def __contains__(zelf, name): try: zelf[name] ; return True except KeyError: return False def __setattr__(zelf, name, value): return dict.__setitem__(zelf, name, value)
[docs] def announce(zelf, *args, **kwargs): """ Announce a text on all channels of all bots. args[0] = "text to announce" >>> import core.bots >>> bot = core.bots.Bot() >>> bot.announce("hello") hello """ from core.kernel import fleet thrs = [] for bot in fleet: thrs.append(zelf.put(bot.announce, *args, **kwargs)) if thrs: zelf.collect(thrs) return thrs ## FLEET
[docs] def search(zelf, *args, **kwargs): """ search all attribute names. args[0] = "attribute name to search for" >>> from core.thing import Thing >>> thing = Thing() >>> thing.data = "test" >>> thing.search("da") ['data'] """ l = [] for key in zelf.names(): if args[0] in key: l.append(key) return l
[docs] def represent(zelf, *args, **kwargs): return ", ".join([str(getattr(zelf, x, None)) for x in zelf.names() if getattr(zelf, x, None)])
[docs] def highest(zelf, *args, **kwargs): name = "" upper = 0 for key, value in zelf.items(): if int(value) > upper: upper = int(value) ; name = key return name
[docs] def lowest(zelf, *args, **kwargs): name = "" lower = 100000000 for key, value in zelf.items(): if int(value) < lower: lower = int(value) ; name = key return name ## STATE
[docs] def define(zelf, *args, **kwargs): """ set a attribute on this ding. """ name = args[0] value = args[1] zelf[name] = value
[docs] def register(zelf, *args, **kwargs): """ callback type with corresponding callback function. """ o = Thing() o.cmnd = args[0] o.func = args[1] o.plugname = get_plugname() if o.cmnd not in zelf: zelf[o.cmnd] = [] todo = [] for obj in zelf[o.cmnd]: if obj.cmnd == o.cmnd and obj.plugname == o.plugname: todo.append(obj) for obj in todo: zelf[o.cmnd].remove(obj) ; logging.info("! remove %s" % name(obj)) zelf[o.cmnd].append(o) logging.debug("- %s/register %s" % (zelf.type, o.cmnd))
[docs] def remove(zelf, *args, **kwargs): name = args[0] if name in zelf: del zelf[name] zelf[name] = [] ## GETTERS
[docs] def find(zelf, *args, **kwargs): rest = args[0] result = [] for x in set(zelf.search(rest)): if x.split(".")[-1] != rest: continue obj = zelf.get(x) if type(obj) in [list, tuple]: result.extend(obj) ; continue result.append(obj) return result
[docs] def by_key(zelf, *args, **kwargs): want = args[0] for key in zelf: if want in key: yield key
[docs] def words(zelf, *args, **kwargs): """ get the arguments of the txt attribute. """ if "txt" in zelf: return zelf.txt.split()
[docs] def days(zelf, *args, **kwargs): """ get the number of days relative to the ding's creation time. """ t1 = time.time() t2 = zelf.get_timed() if t2: time_diff = float(t1 - t2) return elapsed_days(time_diff)
[docs] def dated(zelf, *args, **kwargs): """ retrieve the creation time of an ding. """ val = "" if "Date" in zelf: val = zelf.Date elif "date" in zelf: val = zelf.date elif "published" in zelf: val = zelf.published elif "added" in zelf: val = zelf.added elif "saved" in zelf: val = zelf.saved elif "timed" in zelf: val = zelf.timed return val
[docs] def get_fn(zelf, *args, **kwargs): return os.sep.join(zelf._path.split(os.sep)[-2:])
[docs] def get_timed(zelf, *args, **kwargs): """ retrieve the creation time of an ding. """ t = short_date(zelf.dated()) if t: t = a_time(t) if not t and "_path" in zelf: t = fn_time(zelf._path) if not t: t = fn_time(rtime()) return t
[docs] def get_parsed(zelf, *args, **kwargs): """ parse the txt attribute. """ return txt_parse(zelf.txt, zelf.cc)
[docs] def names(zelf, *args, **kwargs): """ skip the unwanted keys e.g those that start with a "_". """ for key in zelf.keys(): k = str(key) if k.startswith("_"): continue yield key
[docs] def clone(zelf, *args, **kwargs): """ cloned ding, with only the proper keys used. """ res = Thing() for key in zelf.names(): res[key] = zelf[key] return res
[docs] def slice(zelf, *args, **kwargs): """ take a slice of the Thing. """ o = Thing() try: arguments = args[0] except IndexError: arguments = zelf.names() for arg in arguments: try: o[arg] = zelf[arg] except KeyError: continue return o
[docs] def filedatetime(zelf, *args, **kwargs): """ timestamp of related filename. """ return os.sep.join(zelf._path.split(os.sep)[-2:])
[docs] def filedate(zelf, *args, **kwargs): """ timestamp of related filename. """ return zelf._path.split(os.sep)[-2]
[docs] def get_url(zelf, *args, **kwargs): """ url of the Thing's file so that it can be retrieved when API server is running. """ from core.kernel import cfg return "http://%s:%s/%s" % (hostname, cfg.port, zelf.filedatetime())
[docs] def get_root(zelf, *args, **kwargs): from core.kernel import cfg if "workdir" in zelf: root = zelf.workdir else: root = cfg.workdir path = os.path.abspath(root) check_permissions(path) return path
[docs] def get_path(zelf, *args, **kwargs): root = zelf.get_root() if "prefix" in zelf: root = j(root, zelf.prefix) return os.path.abspath(root) ## CHECKERS
[docs] def check_wanted(zelf, *args, **kwargs): """ whether an thing is desired. """ want = args[0] for key, value in want.items(): if key == "format": continue if key not in zelf: continue if value.startswith("-"): continue if value not in str(zelf[key]): return False return True
[docs] def check_notwanted(zelf, *args, **kwargs): """ whether an thing is not desired. """ not_want = args[0] for key, value in not_want.items(): if key == "format": continue if key not in zelf: continue if value in zelf[key]: return True return False ## INPUT
[docs] def load(zelf, *args, **kwargs): """ load a JSON file into this ding. """ if args: path = args[0] else: path = zelf._path ondisk = zelf.read(path) fromdisk = json.loads(ondisk) if "data" in fromdisk: zelf.update(fromdisk["data"]) else: zelf.update(fromdisk) if "saved" in fromdisk: zelf.saved = fromdisk["saved"] zelf._path = path logging.debug("load %s" % path) return zelf
[docs] def read(zelf, *args, **kwargs): """ read the JSON file from disk. """ path = args[0] try: f = open(path, "r") except IOError as ex: if ex.errno == errno.ENOENT: return "{}" raise res = "" for line in f: if not line.strip().startswith("#"): res += line if not res.strip(): return "{}" f.close() return res ## PERSISTENCE
[docs] def prepare(zelf, *args, **kwargs): """ create JSON ready to be saved to disk. """ path = args[0] todisk = Thing() todisk.data = zelf.slice() todisk.saved_from = get_plugname(2) todisk.type = zelf.type todisk.saved = zelf.saved = time.ctime(time.time()) todisk.signature = todisk.data.make_signature() try: result = todisk.json(indent=2, ensure_ascii=False, sort_keys=True) except TypeError: raise NoJSON() return result
[docs] def save(zelf, *args, **kwargs): """ save JSON to disk. """ if not args: t = rtime() else: t = args[0] zelf.sync(j(zelf.get_path(), t)) return t
[docs] def sync(zelf, *args, **kwargs): """ sync JSON to disk. """ try: path = args[0] except IndexError: try: path = zelf._path except AttributeError: path = zelf._path = j(zelf.get_path(), rtime()) logging.warn("! sync %s" % path) d, fn = os.path.split(path) cdir(d) todisk = zelf.prepare(path, **kwargs) 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.close() os.rename(path + ".tmp", path) return zelf ## OUTPUT
[docs] def reply(zelf, *args, **kwargs): """ send reply to origin. """ if "outer" in zelf: zelf.outer.write(str(args[0]) + "\n") ; zelf.outer.flush() ; return if "channel" in zelf: zelf.say(zelf.channel, args[0]) else: zelf.say(zelf.origin, args[0])
[docs] def say(zelf, *args, **kwargs): zelf._target.say(*args, **kwargs)
[docs] def ok(zelf, *args, **kwargs): """ signal ok. """ if "_target" in zelf: zelf.reply("ok %s" % " ".join([str(a) for a in args]))
[docs] def show(zelf, *args, **kwargs): """ list of key,value pairs. """ return ["%s=%s" % (a, zelf[a]) for a in sorted(zelf.names())]
[docs] def display(zelf, *args, **kwargs): parsed = args[0] keys = parsed.args txt = " ".join([str(getattr(zelf, str(key), "")) for key in keys]) txt = txt.rstrip() txt += " - %s" % zelf.days() return txt ## ITERATORS
[docs] def filename(zelf, *args, **kwargs): name = args[0] for fn in zelf.all(): if fn.endswith(name): return fn
[docs] def all(zelf, *args, **kwargs): fns = sorted(list_files(zelf.get_path(), *args, **kwargs), key=lambda fn: fn_time(" ".join(fn.split(os.sep)[-2:]))) res = [] for fn in fns: if kwargs.get("time", "") not in fn: continue res.append(fn) return res
[docs] def first(zelf, *args, **kwargs): fns = zelf.all(*args) for fn in fns: obj = Thing().load(fn) if args and len(args) > 1 and args[1] != obj.get(args[0], ""): continue return obj
[docs] def last(zelf, *args, **kwargs): fns = zelf.all(*args) res = None for fn in fns: obj = Thing().load(fn) if args and len(args) > 1 and args[1] != obj.get(args[0], ""): continue res = obj return res
[docs] def selected(zelf, *args, **kwargs): """ list of desired things. """ parsed = args[0] objs = [] uniqlist = [] for fn in zelf.all(**parsed.wanted): try: obj = Thing().load(fn) except: logging.warn("fail %s" % fn) ; continue if "deleted" in obj and obj.deleted: continue if not obj.check_wanted(parsed.wanted): continue if not obj.selector(parsed.args): continue if obj.check_notwanted(parsed.not_wanted): continue uniq = parsed.switch.get("uniq", "") if uniq: val = obj.get(uniq, "") if val in uniqlist: continue else: uniqlist.append(val) objs.append(obj) return objs
[docs] def period(zelf, *args, **kwargs): parsed = args[0] try: nrofdays = int(parsed.args[0]) except ValueError: return [] seconds = nrofdays * 60 *60 * 24 pasttime = time.time() - seconds result = [] for fnn in list_files(zelf.get_path()): date = fnn.split(os.sep)[-2] stamp = to_day(date) if stamp > pasttime: result.append(fnn) return result ## SELECTOR
[docs] def selector(zelf, *args, **kwargs): """ see if this things has the desired attributes. """ wanted = args[0] if not wanted: return False go = False for arg in wanted: if arg in zelf and zelf[arg]: go = True ; break return go ## WAITERS
[docs] def ready(zelf): """ signal to ready state. """ logging.debug("! %s/ready %s" % (zelf.type, get_location())) zelf._ready.set()
[docs] def clear(zelf): """ clear the ready state. """ logging.debug("! %s/clean %s" % (zelf.type, get_location())) zelf._ready.clear()
[docs] def wait(zelf, sec=180.0): """ wait for ready state. """ logging.debug("! %s/wait %s" % (zelf.type, get_location())) zelf._ready.wait(sec) ## HELPERS
[docs] def json(zelf, *args, **kwargs): """ JSON string representation of this ding. """ return json.dumps(zelf.slice(*args, **kwargs), default=smooth, *args, **kwargs)
[docs] def make_signature(zelf): """ signature of the data contained in this ding. """ return str(hashlib.sha1(bytes(str(zelf), "utf-8")).hexdigest())
[docs] def pretty(zelf, *args, **kwargs): """ nice formatted JSON string of this ding. """ return json.dumps(zelf.slice(), indent=2, default=pretty, sort_keys=True, ensure_ascii=True)