def tree_embeds(t, u, leaves=()): ''' ... >>> tree_embeds([1], [2]) True >>> tree_embeds([1], [1,2]) False >>> tree_embeds([1], [[1,2]]) True >>> tree_embeds([1,[2,3]], [[True, False], 'ab']) True >>> tree_embeds([1,[2,3]], [[True, False], 'ab'], leaves=(str,)) False >>> tree_embeds([], [1,2,3]) False >>> tree_embeds(1, [1,2,3]) True ''' # Pass leaf types around, avoiding infinite regress on strings _is_fork = partial(is_fork, leaves=leaves) if isinstance(t, basestring) or isinstance(u, basestring): leaves += (basestring,) _tree_embeds = partial(tree_embeds, leaves=leaves) if not _is_fork(t): return True else: return (_is_fork(u) and len(list(t)) == len(list(u)) and all(_tree_embeds(n,m) for n,m in zip(t,u)))
def visitTryExcept(self, node): # If we are code after a 'try' block, then it either succeeded or # handled its own exception. So either the 'try' and 'else' blocks ran, # or some of the 'try' and one of the handlers ran. If we treat the # 'try' and 'else' blocks as one, then we can say that # 'try'/'except'/'else' produces unconditional locals when the same # name is local to 'try'/'else' and every handler. All other bindings # are conditional. # Visit children body_v = walk([node.body, node.else_], NameFinder()) # (A handler is (type, name, body) where 'type' and 'name' can be None) handler_vs = [ walk(h, NameFinder()) for h in node.handlers ] assert all(not v.conditional_locals for v in handler_vs) # Free names come from 'try', 'else', and names in 'except' that aren't # bound by the exception name. Since 'handlers' bundles each 'except' # body with its exception name, the bindings are already computed. self._see_unbound(body_v.free | union(v.free for v in handler_vs)) # Unconditional locals only come from locals in both the 'try'/'else' # body and every 'except' body. All other locals are conditional. locals = body_v.locals & intersect(v.locals for v in handler_vs) conditional_locals = \ (body_v.all_locals() | union(v.all_locals() for v in handler_vs)) \ - locals self._bind(locals) self._bind_conditional(conditional_locals)
def tree_embeds(t, u, leaves=()): ''' ... >>> tree_embeds([1], [2]) True >>> tree_embeds([1], [1,2]) False >>> tree_embeds([1], [[1,2]]) True >>> tree_embeds([1,[2,3]], [[True, False], 'ab']) True >>> tree_embeds([1,[2,3]], [[True, False], 'ab'], leaves=(str,)) False >>> tree_embeds([], [1,2,3]) False >>> tree_embeds(1, [1,2,3]) True ''' # Pass leaf types around, avoiding infinite regress on strings _is_fork = partial(is_fork, leaves=leaves) if isinstance(t, basestring) or isinstance(u, basestring): leaves += (basestring, ) _tree_embeds = partial(tree_embeds, leaves=leaves) if not _is_fork(t): return True else: return (_is_fork(u) and len(list(t)) == len(list(u)) and all(_tree_embeds(n, m) for n, m in zip(t, u)))
def is_const(ast): ''' Whether an AST represents a constant expression. I'm not sure what "constant" means yet, but here are some examples: >>> all(is_const(parse(s, mode='eval')) for s in ( ... '0', ... 'True', ... 'None', ... '"foo"', ... '[1,2]', ... '(False, [])', ... '{"a": 1}', ... '{"a": 0, (True, False): [None, 3, "fish", ()]}', ... )) True And some non-examples, some of which maybe should be reclassified: >>> any(is_const(parse(s, mode='eval')) for s in ( ... '0+1', ... '~8', ... '0 < 1', ... 'not True', ... '"fo%s" % "o"', ... 'len([1,2])', ... '[1,2][0]', ... '[1,a]', ... '[a for a in [1,2]]', ... '{"a": 0}.keys()', ... 'set()', ... 'list()', ... 'dict()', ... 'dict', ... 'lambda: 3', ... 'lambda a: a', ... )) False ''' return ( isinstance(ast, Const) or isinstance(ast, Name) and ast.name in ['None', 'True', 'False'] or isinstance(ast, (List, Tuple, Dict)) and all(map(is_const, ast)) or isinstance(ast, Expression) and is_const(ast.node) )
def tree_zip(*trees, **kw): ''' Zip recursively. Returns nested lists with tuples at the bottom. >>> tree_zip([1,[2,3]], [5,[6,7]]) [(1, 5), [(2, 6), (3, 7)]] >>> tree_zip([1,[2,3]], [5,[6,7]], ['a',['b','c']]) [(1, 5, 'a'), [(2, 6, 'b'), (3, 7, 'c')]] >>> tree_zip('foo', 'bar') [('f', 'b'), ('o', 'a'), ('o', 'r')] >>> tree_zip(['foo'], ['bar']) [[('f', 'b'), ('o', 'a'), ('o', 'r')]] >>> tree_zip(['foo'], ['bar'], leaves=(str,)) [('foo', 'bar')] >>> tree_zip('foo', ['bar']) [('f', 'bar')] >>> tree_zip('foo', ['bar'], leaves=(str,)) ('foo', ['bar']) >>> tree_zip(1, 2) (1, 2) >>> tree_zip([1,[2,3]], [5,6,7,8], [['a','b'],['c','d']]) [(1, 5, ['a', 'b']), ([2, 3], 6, ['c', 'd'])] ''' # (Default arguments) leaves = kw.get('leaves', ()) # Pass leaf types around, avoiding infinite regress on strings _is_fork = partial(is_fork, leaves=leaves) if any(isinstance(t, basestring) for t in trees): leaves += (basestring,) _tree_zip = partial(tree_zip, leaves=leaves) if not all(_is_fork(t) for t in trees): return trees else: return [ _tree_zip(*neighbors) for neighbors in zip(*trees) ]
def tree_zip(*trees, **kw): ''' Zip recursively. Returns nested lists with tuples at the bottom. >>> tree_zip([1,[2,3]], [5,[6,7]]) [(1, 5), [(2, 6), (3, 7)]] >>> tree_zip([1,[2,3]], [5,[6,7]], ['a',['b','c']]) [(1, 5, 'a'), [(2, 6, 'b'), (3, 7, 'c')]] >>> tree_zip('foo', 'bar') [('f', 'b'), ('o', 'a'), ('o', 'r')] >>> tree_zip(['foo'], ['bar']) [[('f', 'b'), ('o', 'a'), ('o', 'r')]] >>> tree_zip(['foo'], ['bar'], leaves=(str,)) [('foo', 'bar')] >>> tree_zip('foo', ['bar']) [('f', 'bar')] >>> tree_zip('foo', ['bar'], leaves=(str,)) ('foo', ['bar']) >>> tree_zip(1, 2) (1, 2) >>> tree_zip([1,[2,3]], [5,6,7,8], [['a','b'],['c','d']]) [(1, 5, ['a', 'b']), ([2, 3], 6, ['c', 'd'])] ''' # (Default arguments) leaves = kw.get('leaves', ()) # Pass leaf types around, avoiding infinite regress on strings _is_fork = partial(is_fork, leaves=leaves) if any(isinstance(t, basestring) for t in trees): leaves += (basestring, ) _tree_zip = partial(tree_zip, leaves=leaves) if not all(_is_fork(t) for t in trees): return trees else: return [_tree_zip(*neighbors) for neighbors in zip(*trees)]