예제 #1
0
class RuleSet(object):
    """An observable, stably-ordered collection of rules"""
    default_action = NoApplicableMethods()
    default_actiontype = Method

    def __init__(self, lock=None):
        self.rules = []
        self.actiondefs = {}
        self.listeners = []
        if lock is not None:
            self.__lock__ = lock

    synchronized()

    def add(self, rule):
        actiondefs = frozenset(self._actions_for(rule))
        self.rules.append(rule)
        self.actiondefs[rule] = actiondefs
        self._notify(added=actiondefs)

    synchronized()

    def remove(self, rule):
        actiondefs = self.actiondefs.pop(rule)
        self.rules.remove(rule)
        self._notify(removed=actiondefs)

    #def changed(self, rule):
    #    sequence, actions = self.actions[rule]
    #    new_actions = frozenset(self._actions_for(rule, sequence))
    #    self.actions[rule] = sequence, new_actions
    #    self.notify(new_actions-actions, actions-new_actions)

    synchronized()

    def clear(self):
        actiondefs = frozenset(self)
        del self.rules[:]
        self.actiondefs.clear()
        self._notify(removed=actiondefs)

    def _notify(self, added=empty, removed=empty):
        for listener in self.listeners[:]:  # must be re-entrant
            listener.actions_changed(added, removed)

    synchronized()

    def __iter__(self):
        ad = self.actiondefs
        return iter([a for rule in self.rules for a in ad[rule]])

    def _actions_for(self, (na, body, predicate, actiontype, seq)):
        actiontype = actiontype or self.default_actiontype
        for signature in disjuncts(predicate):
            yield Rule(body, signature, actiontype, seq)
예제 #2
0
    def _notify(self, added=empty, removed=empty):
        for listener in self.listeners[:]:  # must be re-entrant
            listener.actions_changed(added, removed)

    synchronized()
    def __iter__(self):
        ad = self.actiondefs
        return iter([a for rule in self.rules for a in ad[rule]])

    def _actions_for(self, (na, body, predicate, actiontype, seq)):
        actiontype = actiontype or self.default_actiontype
        for signature in disjuncts(predicate):
            yield Rule(body, signature, actiontype, seq)

    synchronized()
    def subscribe(self, listener):
        self.listeners.append(listener)
        if self.rules:
            listener.actions_changed(frozenset(self), empty)

    synchronized()
    def unsubscribe(self, listener):
        self.listeners.remove(listener)


def _register_rule(gf, pred, context, cls):
    """Register a rule for `gf` with possible import-deferring"""
    if not isinstance(gf, basestring):
        rules = rules_for(gf)
        rules.add(parse_rule(Dispatching(gf).engine, pred, context, cls))
예제 #3
0
파일: core.py 프로젝트: zjw0358/PEAK-Rules
class Engine(object):
    """Abstract base for dispatching engines"""

    reset_on_remove = True

    def __init__(self, disp):
        self.function = disp.function
        self.registry = {}
        self.closures = {}
        self.rules = disp.rules
        self.__lock__ = disp.get_lock()
        self.argnames = list(
            flatten(filter(None, inspect.getargspec(self.function)[:3]))
        )
        self.rules.subscribe(self)

    synchronized()
    def actions_changed(self, added, removed):
        if removed and self.reset_on_remove:
            return self._full_reset()
        for rule in removed:
            self._remove_method(rule.predicate, rule)
        for rule in added:
            self._add_method(rule.predicate, rule)
        if added or removed:
            self._changed()

    def _changed(self):
        """Some change to the rules has occurred"""
        Dispatching(self.function).request_regeneration()

    def _full_reset(self):
        """Regenerate any code, caches, indexes, etc."""
        self.registry.clear()
        self.actions_changed(self.rules, ())
        Dispatching(self.function).request_regeneration()





    compiled_cache = None

    def apply_template(self, template, *args):
        try:
            return self.compiled_cache[template, args]
        except (KeyError, TypeError, AttributeError):
            pass

        try:
            closure = self.closures[template]
        except KeyError:
            if getattr(template, CLOSURE):
                raise TypeError("Templates cannot use outer-scope variables")
            import linecache; from peak.util.decorators import cache_source
            tmp = apply_template(template, self.function, *args)
            body = ''.join(linecache.getlines(getattr(tmp, CODE).co_filename))
            filename = "<%s at 0x%08X wrapping %s at 0x%08X>" % (
                template.__name__, id(template),
                self.function.__name__, id(self)
            )
            d ={}
            exec(compile(body, filename, "exec"), getattr(template, GLOBALS), d)
            tmp, closure = d.popitem()
            setattr(closure, DEFAULTS, getattr(template, DEFAULTS))
            cache_source(filename, body, closure)
            self.closures[template] = closure
        f = closure(self.function, *args)
        setattr(f, DEFAULTS, getattr(self.function, DEFAULTS))

        try:
            hash(args)
        except TypeError:
            pass
        else:
            if self.compiled_cache is None:
                from weakref import WeakValueDictionary
                self.compiled_cache = WeakValueDictionary()
            self.compiled_cache[template, args] = f
        return f


    def _add_method(self, signature, rule):
        """Add a case for the given signature and rule"""
        registry = self.registry
        action = rule.actiontype(rule.body, signature, rule.sequence)
        if signature in registry:
            registry[signature] = combine_actions(registry[signature], action)
        else:
            registry[signature] = action
        return action

    def _remove_method(self, signature, rule):
        """Remove the case for the given signature and rule"""
        raise NotImplementedError

    def _generate_code(self):
        """Return a code object for the current state of the function"""
        raise NotImplementedError
예제 #4
0
파일: core.py 프로젝트: zjw0358/PEAK-Rules
class Dispatching(AddOn):
    """Manage a generic function's rules, engine, locking, and code"""
    engine = None
    def __init__(self, func):
        func.__doc__    # workaround for PyPy issue #1293
        self.function = func
        self._regen   = self._regen_code()  # callback to regenerate code
        self.rules    = RuleSet(self.get_lock())
        self.backup   = None  # allows func to call itself during regeneration
        self.create_engine(TypeEngine)

    synchronized()
    def get_lock(self):
        return self.__lock__

    def create_engine(self, engine_type):
        """Create a new engine of `engine_type`, unsubscribing old"""
        if self.engine is not None and self.engine in self.rules.listeners:
            self.rules.unsubscribe(self.engine)
        self.engine = engine_type(self)
        return self.engine

    synchronized()
    def request_regeneration(self):
        """Ensure code regeneration occurs on next call of the function"""
        if self.backup is None:
            self.backup = getattr(self.function, CODE)
            setattr(self.function, CODE, self._regen)
    def _regen_code(self):
        c = Code.from_function(self.function, copy_lineno=True)
        c.return_(
            call_thru(
                self.function,
                Call(Getattr(
                    Call(Const(Dispatching), (Const(self.function),), fold=False),
                    '_regenerate'
                ))
            )
        )
        return c.code()

    synchronized()
    def as_abstract(self):
        for action in self.rules:
            raise AssertionError("Can't make abstract: rules already exist")

        c = Code.from_function(self.function, copy_lineno=True)
        c.return_(call_thru(self.function, Const(self.rules.default_action)))

        if self.backup is None:
            setattr(self.function, CODE, c.code())
        else:
            self.backup = c.code()
        return self.function

    synchronized()
    def _regenerate(self):
        func = self.function
        assert self.backup is not None
        setattr(func, CODE, self.backup)    # ensure re-entrant calls work

        try:
            # try to replace the code with new code
            setattr(func, CODE, self.engine._generate_code())
        except:
            # failure: we'll try to regen again, next time we're called
            setattr(func, CODE, self._regen)
            raise
        else:
            # success!  get rid of the old backup code and return the function
            self.backup = None
            return func
예제 #5
0
    def _notify(self, added=empty, removed=empty):
        for listener in self.listeners[:]:  # must be re-entrant
            listener.actions_changed(added, removed)

    synchronized()

    def __iter__(self):
        ad = self.actiondefs
        return iter([a for rule in self.rules for a in ad[rule]])

    def _actions_for(self, (na, body, predicate, actiontype, seq)):
        actiontype = actiontype or self.default_actiontype
        for signature in disjuncts(predicate):
            yield Rule(body, signature, actiontype, seq)

    synchronized()

    def subscribe(self, listener):
        self.listeners.append(listener)
        if self.rules:
            listener.actions_changed(frozenset(self), empty)

    synchronized()

    def unsubscribe(self, listener):
        self.listeners.remove(listener)


def _register_rule(gf, pred, context, cls):
    """Register a rule for `gf` with possible import-deferring"""
    if not isinstance(gf, basestring):
예제 #6
0
class IndexedEngine(Engine, TreeBuilder):
    """A dispatching engine that builds trees using bitmap indexes"""
    def __init__(self, disp):
        self.signatures = []
        self.all_exprs = {}
        super(IndexedEngine, self).__init__(disp)
        self.arguments = dict([(arg, Local(arg)) for arg in self.argnames])

    def _add_method(self, signature, rule):
        signature = Signature(tests_for(signature, self))
        if signature not in self.registry:
            case_id = len(self.signatures)
            self.signatures.append(signature)
            requires = []
            exprs = self.all_exprs
            for _t, expr, criterion in tests_for(signature, self):
                Ordering(self, expr).requires(requires)
                requires.append(expr)
                index_type = bitmap_index_type(self, expr)
                if index_type is not None:
                    if expr not in exprs:
                        exprs[expr] = 1
                        if always_testable(expr):
                            Ordering(self, expr).requires([])
                    index_type(self, expr).add_case(case_id, criterion)
        return super(IndexedEngine, self)._add_method(signature, rule)

    def _generate_code(self):
        smig = SMIGenerator(self.function)
        all_exprs = map(self.to_expression, self.all_exprs)
        for expr in all_exprs:
            smig.maybe_cache(expr)

        memo = dict([(expr, smig.action_id(expr)) for expr in all_exprs])
        return smig.generate(self.build_root(memo)).func_code

    def _full_reset(self):
        # Replace the entire engine with a new one
        Dispatching(self.function).create_engine(self.__class__)

    synchronized()

    def seed_bits(self, expr, cases):
        return BitmapIndex(self, expr).seed_bits(cases)

    synchronized()

    def reseed(self, expr, criterion):
        return BitmapIndex(self, expr).reseed(criterion)

    # Make build() a synchronized method
    build = synchronized(TreeBuilder.build.im_func)

    def build_root(self, memo):
        return self.build(
            to_bits([len(self.signatures)]) - 1, frozenset(self.all_exprs),
            memo)

    def best_expr(self, cases, exprs):
        return super(IndexedEngine, self).best_expr(list(from_bits(cases)),
                                                    exprs)

    def build_node(self, expr, cases, remaining_exprs, memo):
        return memo[expr], predicate_node_for(self, expr, cases,
                                              remaining_exprs, memo)

    def selectivity(self, expr, cases):
        return BitmapIndex(self, expr).selectivity(cases)

    def to_expression(self, expr):
        return expr

    def build_leaf(self, cases, memo):
        action = self.rules.default_action
        signatures = self.signatures
        registry = self.registry
        for case_no in from_bits(cases):
            action = combine_actions(action, registry[signatures[case_no]])
        # No need to memoize here, since the combined action probably isn't
        # a meaningful key, and template-compiled methods are memoized at a
        # lower level anyway.
        return (0, compile_method(action, self))