Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

AST as Black Magic

AST as Black Magic

PyCon APAC 2023 - Unconference Track 2 4:35 pm

Takumi Sueda

October 27, 2023
Tweet

More Decks by Takumi Sueda

Other Decks in Technology

Transcript

  1. PyCon APAC 2 0 2 3 Unconference Track 2 ,

    starts at 4 : 35 pm AST as Black Magic Takumi Sueda @puhitaku
  2. from defer import defers @defers def func(): print(1) defer: print(3)

    print(2) func() defer.py Deferring a function call as seen in Go ... but Python can do it too with AST magic 4 1 2 3 Code Output
  3. from defer import defers @defers def func(): print(1) defer: print(3)

    print(2) func() defer.py This highlighted line (mis)uses "Variable annotations" proposed in PEP 52 6 . Variable annotations can accept arbitrary expression as the annotation. @defers decorater replaces the annotation line to a deferred func call. 5
  4. defer.py Possible usage of defer.py 6 def func(): with foo()

    as a: with bar() as b: with baz() as c: . .. @defers def func(): a = foo() defer: a.close() b = bar() defer: b.close() c = baz() defer: c.close() ... You can turn this ... ... into this
  5. defer.py A totally valid Python script and the output 8

    @conversationize def foo(): Alice: "Hi there" Bob: "Ayo, sup" Xzibit: "Yo dawg, I heard you like Python so I put Python in yo Python so you can eval while you eval" Alice says: Hi there Bob says: Ayo, sup Xzibit says: Yo dawg, I heard you like Python so I put Python in yo Python so you can eval while you eval
  6. import ast import inspect def conversationize(f): class Transformer(ast.NodeTransformer): def visit_AnnAssign(self,

    node): print(f'{node.target.id} says: {node.annotation.value}') def wrapper(): tree = ast.parse(inspect.getsource(f)) assert len(tree.body) == 1 and isinstance(tree.body[0], ast.FunctionDef), "should be a function" Transformer().visit(tree.body[0]) return wrapper @conversationize def foo(): Alice: "Hi there" 11 The implementation of the example decorator "conversationize"