def test_schema(): from syn.schema.b.sequence import Sequence from syn.type.a import List t = Schema(Sequence(1, 2, 3)) assert t == Schema(Sequence(1, 2, 3)) assert t != Schema(Sequence(1, 3, 2)) assert Type.dispatch(t) is t assert t.query([1, 2, 3]) assert not t.query([1, 3, 2]) t.validate([1, 2, 3]) assert_raises(TypeError, t.validate, [1, 3, 2]) assert t.generate() == [1, 2, 3] assert t.display() == t.rst() == '<Schema>' assert t.coerce(1) == 1 t = Schema(Sequence(int, float)) assert t.query([1, 2.3]) assert not t.query([1, 2]) val = t.generate() assert t.query(val) t = Schema(Sequence(int, List(float))) assert not t.query([1, 1.2]) assert not t.query([1, [1, 2]]) assert t.query([1, [1.2, 3.4]]) assert t.query([1, []]) val = t.generate() assert t.query(val)
class FunctionDef(Block): _opts = dict(args=['name', 'args', 'body', 'decorator_list']) _attrs = dict(name=Attr(STR, group=AST), args=Attr(Arguments, groups=(AST, ACO)), decorator_list=OAttr(List(Expression), groups=(AST, ACO, CC))) if VER >= '3': _attrs['returns'] = OAttr(Expression, groups=(AST, ACO)) _opts['args'].append('returns') def emit_decorators(self, **kwargs): if not self.decorator_list: return '' pre = self._indent(**kwargs) with setitem(kwargs, 'indent_level', 0): strs = [ pre + '@' + dec.emit(**kwargs) for dec in self.decorator_list ] return '\n'.join(strs) + '\n' def emit(self, **kwargs): ret = self.emit_decorators(**kwargs) head = 'def ' + self.name + '(' + self.args.emit(**kwargs) + ')' if VER >= '3': if self.returns: with setitem(kwargs, 'indent_level', 0): head += ' -> {}'.format(self.returns.emit(**kwargs)) ret += self.emit_block(head, self.body, **kwargs) return ret
class Block(Statement): _attrs = dict( body=Attr(List((Expression, Statement)), groups=(AST, ACO, CC))) _opts = dict(max_len=0) def emit_block(self, head, body, **kwargs): ret = self._indent(**kwargs) ret += head + ':\n' level = kwargs.get('indent_level', 0) with setitem(kwargs, 'indent_level', level + 1): strs = [elem.emit(**kwargs) for elem in body] ret += '\n'.join(strs) return ret def valuify_block(self, body, name, **kwargs): child = body[-1] if isinstance(child, Assign): if name in child.targets: return if not isinstance(child, Expression): child = child.as_value(**kwargs).resolve_progn(**kwargs) body[-1] = Assign([name], child)
class Compare(Expression): _opts = dict(args=('left', 'ops', 'comparators')) _attrs = dict(left=Attr(Expression, groups=(AST, ACO)), ops=Attr(List(Comparator), groups=(AST, ACO, CC)), comparators=Attr(List(Expression), groups=(AST, ACO, CC))) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): left = self.left.emit(**kwargs) ops = [op.emit(**kwargs) for op in self.ops] comps = [comp.emit(**kwargs) for comp in self.comparators] ret = left for op, comp in zip(ops, comps): ret += ' {} {}'.format(op, comp) ret = '(' + ret + ')' ret = self._indent(**kwargs) + ret return ret
class Function(SyntagmathonNode): _attrs = dict(name=Attr((Variable, STR)), signature=Attr(List(Variable)), body=Attr((List(SyntagmathonNode), tuple)), placeholder=Attr(bool, False)) _opts = dict(args=('name', 'signature', 'body')) def __call__(self, *args_, **kwargs): args = {} for k, arg in enumerate(args_): args[self.signature[k].name] = arg for key, value in kwargs.items(): if key in args: raise TypeError("Parameter {} specified twice".format(key)) args[key] = value return Call(self, args) def call(self, env, **kwargs): return eval(self.body, env, **kwargs) def eval(self, env, **kwargs): env[self.get_name()] = self return self def get_name(self): return self.name if isinstance(self.name, STR) else self.name.name def to_python(self, **kwargs): from syn.python.b import Arguments, FunctionDef, Pass if VER < '3': args = Arguments( [to_python(arg, **kwargs) for arg in self.signature]) else: from syn.python.b import Arg args = Arguments([Arg(arg.name) for arg in self.signature]) body = to_python(self.body, **kwargs) if not body: body = [Pass()] body[-1] = body[-1].as_return() return FunctionDef(self.name, args, body)
class Import(Statement): _attrs = dict(names=Attr(List(Alias), groups=(AST, ACO, CC))) _opts = dict(args=('names', )) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): strs = [val.emit(**kwargs) for val in self.names] names = ', '.join(strs) ret = self._indent(**kwargs) ret += 'import ' + names return ret
class Call(Expression): _opts = dict(args=['func', 'args', 'keywords']) _attrs = dict(func=Attr(Expression, groups=(AST, ACO)), args=OAttr(List(Expression), groups=(AST, ACO, CC)), keywords=OAttr(List(Keyword), groups=(AST, ACO, CC))) if VER < '3.5': _attrs['starargs'] = OAttr(PythonNode, groups=(AST, ACO)) _attrs['kwargs'] = OAttr(PythonNode, groups=(AST, ACO)) _opts['args'].extend(['starargs', 'kwargs']) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): func = self.func.emit(**kwargs) args = [arg.emit(**kwargs) for arg in self.args] if self.args else [] kwds = [kw.emit(**kwargs) for kw in self.keywords] if self.keywords else [] if VER < '3.5': starargs = self.starargs.emit( **kwargs) if self.starargs else '' kwargs_ = self.kwargs.emit(**kwargs) if self.kwargs else '' else: starargs = '' kwargs_ = '' strs = [] ret = self._indent(**kwargs) + func + '(' if args: strs.extend(args) if kwds: strs.extend(kwds) if starargs: strs.append('*' + starargs) if kwargs_: strs.append('**' + kwargs_) ret += ', '.join(strs) ret += ')' return ret
class BoolOp(Expression): _opts = dict(args=('op', 'values')) _attrs = dict(op=Attr(BooleanOperator, groups=(AST, ACO)), values=Attr(List(Expression), groups=(AST, ACO, CC))) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): op = ' ' + self.op.emit(**kwargs) + ' ' vals = [val.emit(**kwargs) for val in self.values] ret = op.join(vals) ret = '(' + ret + ')' ret = self._indent(**kwargs) + ret return ret
class Sequence(Literal): bounds = ('[', ']') delim = ', ' _opts = dict(args = ('elts',)) _attrs = dict(elts = Attr(List(Expression), groups=(AST, ACO, CC))) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): cs = [c.emit(**kwargs) for c in self.elts] ret = self.delim.join(cs) if len(cs) == 1 and isinstance(self, Tuple): ret += ',' ret = self.bounds[0] + ret + self.bounds[1] ret = self._indent(**kwargs) + ret return ret
class Env(Base): _attrs = dict(frames=Attr(List(Frame), init=lambda self: list([Frame()]))) _opts = dict(init_validate=True) current_frame = property(lambda self: self.frames[-1]) def __getitem__(self, key): return self.current_frame[key] def __setitem__(self, key, value): self.current_frame[key] = value def __delitem__(self, key): del self.current_frame[key] def __iter__(self): return iter(self.current_frame) def __len__(self): return len(self.current_frame) def items(self): for item in self.current_frame.items(): yield item def gensym(self): pass def globals(self): return dict(self.current_frame.globals) def locals(self): return dict(self.current_frame.locals) def push(self, dct): globs = self.globals() globs.update(self.locals()) self.frames.append(Frame(globals=globs, locals=dct)) def pop(self): return self.frames.pop() def set_global(self, key, value): self.current_frame.set_global(key, value) def update(self, dct): self.current_frame.update(dct)
class Assign(Statement): _attrs = dict(targets=Attr(List(Expression), groups=(AST, ACO, CC)), value=Attr(Expression, groups=(AST, ACO))) _opts = dict(args=('targets', 'value')) @logging(AsValue) def as_value(self, **kwargs): return ProgN(self.copy()) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): targs = [targ.emit(**kwargs) for targ in self.targets] val = self.value.emit(**kwargs) ret = self._indent(**kwargs) ret += ' = '.join(targs) ret += ' = ' + val return ret
class While(Block): _attrs = dict(test=Attr(Expression, groups=(AST, ACO)), orelse=Attr(List((Expression, Statement)), groups=(AST, ACO, CC), init=lambda self: list())) _opts = dict(args=('test', 'body', 'orelse')) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): head = 'while ' + self.test.emit(**kwargs) ret = self.emit_block(head, self.body, **kwargs) if self.orelse: head = 'else' block = self.emit_block(head, self.orelse, **kwargs) ret += '\n' + block return ret
class For(Block): _attrs = dict(target=Attr((Name, Tuple, List_), groups=(AST, ACO)), iter=Attr(Expression, groups=(AST, ACO)), orelse=Attr(List((Expression, Statement)), groups=(AST, ACO, CC), init=lambda self: list())) _opts = dict(args=('target', 'iter', 'body', 'orelse')) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): head = 'for {} in {}'.format(self.target.emit(**kwargs), self.iter.emit(**kwargs)) ret = self.emit_block(head, self.body, **kwargs) if self.orelse: head = 'else' block = self.emit_block(head, self.orelse, **kwargs) ret += '\n' + block return ret
def _harvest_attrs(clsdata): getfunc(Harvester._harvest_attrs)(clsdata) dct = {} clsdct = clsdata['dct'] attrs = clsdct.get('_attrs', {}) types = clsdct.get('types', []) schema = clsdct.get('schema', None) if types and schema: raise TypeError( 'Cannot specify both types and schema in {}'.format( clsdata['clsname'])) if types: dct['_list'] = Attr(List(tuple(types))) elif schema: dct['_list'] = Attr(Schema(schema)) preserve_attr_data(attrs, dct) attrs.update(dct) clsdct['_attrs'] = attrs
class Tree(Base): _attrs = dict( root=Attr(Node, init=lambda self: Node(), doc="The root node of the tree"), nodes=Attr(List(Node), init=lambda self: list(), groups=(GENEX, STREX), doc="List of all tree nodes"), node_types=Attr(List(STR), init=lambda self: list(), groups=(GENEX, STREX), doc="List of all tree node types"), id_dict=Attr(Dict(Node), init=lambda self: dict(), groups=(GENEX, STREX), doc="Mapping of ids to nodes"), type_dict=Attr(Dict(List(Node)), init=lambda self: dict(), groups=(GENEX, STREX), doc="Mapping of type names to node lists"), node_counter=Attr(Counter, init=lambda self: Counter(), groups=(EQEX, GENEX, STREX), doc='Node id counter'), ) _opts = dict(init_validate=True, args=('root', )) def __init__(self, *args, **kwargs): super(Tree, self).__init__(*args, **kwargs) self.add_node(self.root) def add_node(self, node, **kwargs): if node in self.nodes: raise TreeError("Node '{}' already in tree; cannot add".format( repr(node))) def add_node(_node): if _node._id is None: _node._id = self.node_counter() self.id_dict[_node._id] = _node self.nodes.append(_node) if get_typename(_node) not in self.node_types: self.node_types.append(get_typename(_node)) if get_typename(_node) not in self.type_dict: self.type_dict[get_typename(_node)] = [] self.type_dict[get_typename(_node)].append(_node) if node != self.root: parent = getitem(kwargs, 'parent', None, True) if not parent: parent = self.get_node_by_id( getitem(kwargs, 'parent_id', max(self.id_dict))) if parent not in self.nodes: raise TreeError("Parent node '{}' not in tree".format( repr(parent))) parent.add_child(node) self.depth_first(node=add_node, current_node=node) def remove_node(self, node, **kwargs): if node not in self.nodes: raise TreeError("Node '{}' not in tree; cannot remove".format( repr(node))) def remove_node(_node): del self.id_dict[_node._id] self.nodes.remove(_node) self.type_dict[get_typename(_node)].remove(_node) if self.type_dict[get_typename(_node)] == []: self.node_types.remove(get_typename(_node)) del self.type_dict[get_typename(_node)] if node != self.root: parent = node.parent() if parent is None: raise TreeError("Non-root node '{}' has no parent".format( repr(node))) parent.remove_child(node) else: self.root = None self.depth_first(node=remove_node, current_node=node) def replace_node(self, source, dest, **kwargs): if dest.parent() is not None: raise TreeError("Node dest must have no parent") parent = source.parent() if parent is None: if source == self.root: self.remove_node(source, **kwargs) self.root = dest self.add_node(dest, **kwargs) else: raise TreeError("Non-root node '{}' has no parent".format( repr(source))) else: self.remove_node(source, **kwargs) self.add_node(dest, parent=parent, **kwargs) def rebuild(self, **kwargs): '''Repopulate the node-tracking data structures. Shouldn't really ever be needed. ''' self.nodes = [] self.node_types = [] self.id_dict = {} self.type_dict = {} self.add_node(self.root) def get_node_by_id(self, node_id): ret = getitem(self.id_dict, node_id, self._get_node_by_id(node_id), True) return ret def _get_node_by_id(self, node_id): for n in self.nodes: if n._id == node_id: return n return None def _check_search_kwarg_types(self, kwargs): '''Checks that every element of kwargs is a valid type in this tree.''' for key in kwargs: if key not in self.node_types: raise TypeError("Invalid search type: {}".format(key)) def depth_first(self, node=do_nothing, stop_test=do_nothing, _return=identity, current_node='root', **kwargs): self._check_search_kwarg_types(kwargs) if current_node == 'root': current_node = self.root node(current_node) if stop_test(current_node): return _return(current_node) for nodetype, function in kwargs.items(): if get_typename(current_node) == nodetype: function(current_node) for child in current_node.children(): ret = self.depth_first(node, stop_test, _return, child, **kwargs) if ret: return ret def search_rootward(self, node=do_nothing, stop_test=do_nothing, _return=identity, current_node='root', **kwargs): self._check_search_kwarg_types(kwargs) if current_node == 'root': current_node = self.root node(current_node) if stop_test(current_node): return _return(current_node) for nodetype, function in kwargs.items(): if get_typename(current_node) == nodetype: function(current_node) if current_node is self.root: return parent = current_node.parent() return self.search_rootward(node, stop_test, _return, parent, **kwargs) def query(self, q, context=None): if context is None: context = self.root for x in q(context): yield x def find_one(self, *args, **kwargs): try: return first(self.query(*args, **kwargs)) except StopIteration: return None def validate(self): super(Tree, self).validate() self.root.validate()
class SchemaTest(Base): _opts = dict(init_validate=True, args=('a', 'b', 'c')) _attrs = dict(a=Attr(Schema(Sequence(Range(0, 10), float))), b=Attr(Schema(Sequence(int, List(float)))), c=Attr(Set(Range(5, 8))))
class If(Block): _attrs = dict(test=Attr(Expression, groups=(AST, ACO)), orelse=Attr(List((Expression, Statement)), groups=(AST, ACO, CC), init=lambda self: list())) _opts = dict(args=('test', 'body', 'orelse')) def as_return(self, **kwargs): ret = self.copy() ret.body[-1] = ret.body[-1].as_return(**kwargs) if ret.orelse: ret.orelse[-1] = ret.orelse[-1].as_return(**kwargs) ret._set_children() ret._init() return ret @logging(AsValue) def as_value(self, **kwargs): ret = self.copy() var = None if isinstance(ret.body[-1], Assign): var = ret.body[-1].targets[0] if var is None and ret.orelse: if isinstance(ret.orelse[-1], Assign): var = ret.orelse[-1].targets[0] if var is None: if 'gensym' not in kwargs: kwargs['gensym'] = GenSym(ret.variables(**kwargs)) var = Name(kwargs['gensym'].generate()) ret.valuify_block(ret.body, var, **kwargs) if ret.orelse: ret.valuify_block(ret.orelse, var, **kwargs) ret._set_children() ret._init() ret._progn_value = var return ProgN(ret) def emit(self, **kwargs): with setitem(kwargs, 'indent_level', 0): head = 'if ' + self.test.emit(**kwargs) ret = self.emit_block(head, self.body, **kwargs) if self.orelse: head = 'else' block = self.emit_block(head, self.orelse, **kwargs) ret += '\n' + block return ret @logging(ResolveProgN) def resolve_progn(self, **kwargs): temp = self.copy() temp.body = resolve_progn(temp.body, **kwargs) if temp.orelse: temp.orelse = resolve_progn(temp.orelse, **kwargs) temp._set_children() temp._init() kwargs['attr_exclude'] = ['body', 'orelse'] ret = super(If, temp).resolve_progn(**kwargs) return ret
class Arguments(PythonNode): ast = ast.arguments _opts = dict(max_len=0, args=['args', 'vararg', 'kwarg', 'defaults']) _attrs = dict(args=Attr(List(Name), groups=(AST, ACO, CC)), vararg=OAttr(STR, group=AST), kwarg=OAttr(STR, group=AST), defaults=Attr(List(Expression), groups=(AST, ACO, CC), init=lambda self: list())) if VER >= '3.4': _attrs = dict(args=Attr(List(Arg), groups=(AST, ACO, CC)), kwonlyargs=Attr(List(Arg), groups=(AST, ACO, CC), init=lambda self: list()), vararg=OAttr(Arg, groups=(AST, ACO)), kwarg=OAttr(Arg, groups=(AST, ACO)), defaults=Attr(List(Expression), groups=(AST, ACO, CC), init=lambda self: list()), kw_defaults=Attr(List((Expression, type(None))), groups=(AST, ACO, CC), init=lambda self: list())) _opts['args'] = [ 'args', 'vararg', 'kwonlyargs', 'kwarg', 'defaults', 'kw_defaults' ] def emit2(self, **kwargs): with setitem(kwargs, 'indent_level', 0): n_defs = len(self.defaults) N = len(self.args) - n_defs strs = [self.args[k].emit(**kwargs) for k in xrange(N)] strs += [ '{}={}'.format(self.args[k + N].emit(**kwargs), self.defaults[k].emit(**kwargs)) for k in xrange(n_defs) ] if self.vararg: strs.append('*' + self.vararg) if self.kwarg: strs.append('**' + self.kwarg) return ', '.join(strs) def emit3(self, **kwargs): with setitem(kwargs, 'indent_level', 0): n_defs = len(self.defaults) N = len(self.args) - n_defs strs = [self.args[k].emit(**kwargs) for k in xrange(N)] strs += [ '{}={}'.format(self.args[k + N].emit(**kwargs), self.defaults[k].emit(**kwargs)) for k in xrange(n_defs) ] if self.vararg: strs.append('*' + self.vararg.emit(**kwargs)) for kwonly, kwonlydef in zip(self.kwonlyargs, self.kw_defaults): if kwonlydef is not None: strs.append('{}={}'.format(kwonly.emit(**kwargs), kwonlydef.emit(**kwargs))) else: strs.append(kwonly.emit(**kwargs)) if self.kwarg: strs.append('**' + self.kwarg.emit(**kwargs)) return ', '.join(strs) def emit(self, **kwargs): if VER >= '3': return self.emit3(**kwargs) return self.emit2(**kwargs)