Dispelling py.test magic

Dispelling py.test magic

How to replicate py.test magic in lest than 100 lines of code.
Demo code available here: https://github.com/oinopion/dispel

042b05ac1200c78d8009911e302c1ee4?s=128

Tomek Paczkowski

September 19, 2015
Tweet

Transcript

  1. 4.

    def test_doubling(): expected = 5 > assert double(2) == expected

    E assert 4 == 5 E + where 4 = double(2) sample_test.py:6: AssertionError
  2. 5.

    def test_doubling(): expected = 5 > assert double(2) == expected

    E assert 4 == 5 E + where 4 = double(2) sample_test.py:6: AssertionError
  3. 18.

    Goal one Modify the assert statement to
 call a function

    with both sides 
 of the comparison
  4. 21.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  5. 22.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  6. 23.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  7. 24.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  8. 25.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  9. 26.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  10. 27.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  11. 28.

    class AssertRewrite(NodeTransformer):
 def visit_Assert(self, node):
 call = Call(
 func=Name(
 id='assert_equals',

    ctx=Load()
 ),
 args=[
 node.test.left,
 node.test.comparators[0]
 ],
 keywords=[]
 )
 new_node = Expr(value=call)
 copy_location(new_node, node)
 fix_missing_locations(new_node)
 return new_node
  12. 30.

    def transform(module):
 import_node = ImportFrom(
 module='test_utils',
 names=[alias('assert_equals', None)],
 lineno=0, col_offset=0,


    )
 module.body[0:0] = [import_node]
 transformer = AssertRewrite()
 return transformer.visit(module)
  13. 31.

    def transform(module):
 import_node = ImportFrom(
 module='test_utils',
 names=[alias('assert_equals', None)],
 lineno=0, col_offset=0,


    )
 module.body[0:0] = [import_node]
 transformer = AssertRewrite()
 return transformer.visit(module)
  14. 32.

    def transform(module):
 import_node = ImportFrom(
 module='test_utils',
 names=[alias('assert_equals', None)],
 lineno=0, col_offset=0,


    )
 module.body[0:0] = [import_node]
 transformer = AssertRewrite()
 return transformer.visit(module)
  15. 33.

    def transform(module):
 import_node = ImportFrom(
 module='test_utils',
 names=[alias('assert_equals', None)],
 lineno=0, col_offset=0,


    )
 module.body[0:0] = [import_node]
 transformer = AssertRewrite()
 return transformer.visit(module)
  16. 34.

    def transform(module):
 import_node = ImportFrom(
 module='test_utils',
 names=[alias('assert_equals', None)],
 lineno=0, col_offset=0,


    )
 module.body[0:0] = [import_node]
 transformer = AssertRewrite()
 return transformer.visit(module)
  17. 35.

    def transform(module):
 import_node = ImportFrom(
 module='test_utils',
 names=[alias('assert_equals', None)],
 lineno=0, col_offset=0,


    )
 module.body[0:0] = [import_node]
 transformer = AssertRewrite()
 return transformer.visit(module)
  18. 41.

    Goal two Write an import hook 
 that uses our

    transformer 
 to modify imported code
  19. 42.

    def import_hook(path): if os.path.abspath('') == path: return Finder() else: raise

    ImportError sys.path_hooks.insert(0, import_hook) sys.path_importer_cache.clear()
  20. 43.

    def import_hook(path): if os.path.abspath('') == path: return Finder() else: raise

    ImportError sys.path_hooks.insert(0, import_hook) sys.path_importer_cache.clear()
  21. 44.

    def import_hook(path): if os.path.abspath('') == path: return Finder() else: raise

    ImportError sys.path_hooks.insert(0, import_hook) sys.path_importer_cache.clear()
  22. 45.

    def import_hook(path): if os.path.abspath('') == path: return Finder() else: raise

    ImportError sys.path_hooks.insert(0, import_hook) sys.path_importer_cache.clear()
  23. 46.

    def import_hook(path): if os.path.abspath('') == path: return Finder() else: raise

    ImportError sys.path_hooks.insert(0, import_hook) sys.path_importer_cache.clear()
  24. 48.

    from importlib.util import spec_from_file_location
 
 class Finder:
 def find_spec(self, module,

    target=None):
 file_name = module + '.py'
 if not os.path.exists(file_name):
 return None
 return spec_from_file_location(
 name=module, 
 location=file_name, 
 loader=Loader()
 )
  25. 49.

    from importlib.util import spec_from_file_location
 
 class Finder:
 def find_spec(self, module,

    target=None):
 file_name = module + '.py'
 if not os.path.exists(file_name):
 return None
 return spec_from_file_location(
 name=module, 
 location=file_name, 
 loader=Loader()
 )
  26. 50.

    from importlib.util import spec_from_file_location
 
 class Finder:
 def find_spec(self, module,

    target=None):
 file_name = module + '.py'
 if not os.path.exists(file_name):
 return None
 return spec_from_file_location(
 name=module, 
 location=file_name, 
 loader=Loader()
 )
  27. 51.

    from importlib.util import spec_from_file_location
 
 class Finder:
 def find_spec(self, module,

    target=None):
 file_name = module + '.py'
 if not os.path.exists(file_name):
 return None
 return spec_from_file_location(
 name=module, 
 location=file_name, 
 loader=Loader()
 )
  28. 52.

    from importlib.util import spec_from_file_location
 
 class Finder:
 def find_spec(self, module,

    target=None):
 file_name = module + '.py'
 if not os.path.exists(file_name):
 return None
 return spec_from_file_location(
 name=module, 
 location=file_name, 
 loader=Loader()
 )
  29. 53.

    from importlib.util import spec_from_file_location
 
 class Finder:
 def find_spec(self, module,

    target=None):
 file_name = module + '.py'
 if not os.path.exists(file_name):
 return None
 return spec_from_file_location(
 name=module, 
 location=file_name, 
 loader=Loader()
 )
  30. 55.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as fp:

    source = fp.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  31. 56.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as fp:

    source = fp.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  32. 57.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as f:

    source = f.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  33. 58.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as fp:

    source = fp.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  34. 59.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as fp:

    source = fp.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  35. 60.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as fp:

    source = fp.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  36. 61.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as fp:

    source = fp.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  37. 62.

    class Loader: def exec_module(self, module): with open(module.__file__, 'rb') as fp:

    source = fp.read() tree = ast.parse(source, module.__file__) tree = transform(tree) code = compile(tree, module.__file__, 'exec') exec(code, module.__dict__)
  38. 63.

    class Loader:
 def exec_module(self, module):
 with open(module.__file__, 'rb') as fp:


    source = fp.read()
 
 tree = ast.parse(source, module.__file__)
 tree = transform(tree)
 code = compile(tree, module.__file__, 'exec')
 module.__dict__['#eq'] = assert_equals
 exec(code, module.__dict__) Bonus
  39. 66.
  40. 67.

    Summary This is probably a giant foot gun Corner cases

    left for the reader Python is awesome