# -*- coding: utf-8 -*- #!/usr/bin/env python import types class Node: pass from redis import Redis class NodeVisitor: def visit(self, node): stack = [ node ] last_result = None while stack: try: last = stack[-1] if isinstance(last, types.GeneratorType): stack.append(last.send(last_result)) last_result = None elif isinstance(last, Node): stack.append(self._visit(stack.pop())) else: last_result = stack.pop() except StopIteration: stack.pop() return last_result def _visit(self, node): methname = 'visit_' + type(node).__name__ meth = getattr(self, methname, None) if meth is None: meth = self.generic_visit return meth(node) def generic_visit(self, node): raise RuntimeError('No {} method'.format('visit_' + type(node).__name__)) # Base class. Uses a descriptor to set a value class Descriptor(object): def __init__(self, name=None, **options): self.name = name for key, value in options.items(): setattr(self, key, value) def __set__(self, instance, value): instance.__dict__[self.name] = value # Descriptor for enforcing types class Typed(Descriptor): expected_type = type(None) def __set__(self, instance, value): if not isinstance(value, self.expected_type): raise TypeError('expected ' + str(self.expected_type)) super(Typed, self).__set__(instance, value) # Descriptor for enforcing values class Unsigned(Descriptor): def __set__(self, instance, value): if value < 0: raise ValueError('Expected >= 0') super(Unsigned, self).__set__(instance, value) class MaxSized(Descriptor): def __init__(self, name=None, **options): if 'size' not in options: raise TypeError('missing size option') super(MaxSized, self).__init__(name, **options) # noinspection PyUnresolvedReferences def __set__(self, instance, value): if len(value) >= self.size: raise ValueError('size must be < ' + str(self.size)) super(MaxSized, self).__set__(instance, value) # Class decorator to apply constraints def check_attributes(**kwargs): def decorate(cls): for key, value in kwargs.items(): if isinstance(value, Descriptor): value.name = key setattr(cls, key, value) else: setattr(cls, key, value(key)) return cls return decorate class LazyRedisConnection: builder = Redis def __init__(self, host, port, db=0): self.host = host self.port = port self.db = db self.client = None def __enter__(self): self.client = self.builder(host=self.host, port=self.port, db=self.db) return self.client def __exit__(self, exc_ty, exc_val, tb): self.client.close() class BaseVisitor(object): def entry(self, node): return type(node).__name__ def visit(self, node): methname = 'visit_' + self.entry(node) meth = getattr(self, methname, None) if meth is None: meth = self.generic_visit return meth(node) def generic_visit(self, node): raise RuntimeError('No {} method'.format('visit_' + type(node).__name__))