pinq.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. from ast import Call
  2. from macropy.core.macros import *
  3. from macropy.core.hquotes import macros, hq, ast, name, ast_list
  4. from macropy.quick_lambda import macros, f, _
  5. import sqlalchemy
  6. macros = Macros()
  7. # workaround for inability to pickle modules
  8. @macros.expr
  9. def sql(tree, **kw):
  10. x = process(tree)
  11. x = expand_let_bindings.recurse(x)
  12. return x
  13. @macros.expr
  14. def query(tree, gen_sym, **kw):
  15. x = process(tree)
  16. x = expand_let_bindings.recurse(x)
  17. sym = gen_sym()
  18. # return q[(lambda query: query.bind.execute(query).fetchall())(ast[x])]
  19. new_tree = hq[(lambda query: name[sym].bind.execute(name[sym]).fetchall())(ast[x])]
  20. new_tree.func.args = arguments([Name(id=sym)], None, None, [])
  21. return new_tree
  22. def process(tree):
  23. @Walker
  24. def recurse(tree, **kw):
  25. if type(tree) is Compare and type(tree.ops[0]) is In:
  26. return hq[(ast[tree.left]).in_(ast[tree.comparators[0]])]
  27. elif type(tree) is GeneratorExp:
  28. aliases = map(f[_.target], tree.generators)
  29. tables = map(f[_.iter], tree.generators)
  30. aliased_tables = map(lambda x: hq[(ast[x]).alias().c], tables)
  31. elt = tree.elt
  32. if type(elt) is Tuple:
  33. sel = hq[ast_list[elt.elts]]
  34. else:
  35. sel = hq[[ast[elt]]]
  36. out = hq[sqlalchemy.select(ast[sel])]
  37. for gen in tree.generators:
  38. for cond in gen.ifs:
  39. out = hq[ast[out].where(ast[cond])]
  40. out = hq[(lambda x: ast[out])()]
  41. out.func.args.args = aliases
  42. out.args = aliased_tables
  43. return out
  44. return recurse.recurse(tree)
  45. def generate_schema(engine):
  46. metadata = sqlalchemy.MetaData(engine)
  47. metadata.reflect()
  48. class Db: pass
  49. db = Db()
  50. for table in metadata.sorted_tables:
  51. setattr(db, table.name, table)
  52. return db
  53. @Walker
  54. def _find_let_bindings(tree, ctx, stop, collect, **kw):
  55. if type(tree) is Call and type(tree.func) is Lambda:
  56. stop()
  57. collect(tree)
  58. return tree.func.body
  59. elif type(tree) in [Lambda, GeneratorExp, ListComp, SetComp, DictComp]:
  60. stop()
  61. return tree
  62. @Walker
  63. def expand_let_bindings(tree, **kw):
  64. tree, chunks = _find_let_bindings.recurse_collect(tree)
  65. for v in chunks:
  66. let_tree = v
  67. let_tree.func.body = tree
  68. tree = let_tree
  69. return tree