Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dispelling py.test magic

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

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

Avatar for Tomek Paczkowski

Tomek Paczkowski

September 19, 2015

More Decks by Tomek Paczkowski

Other Decks in Programming

Transcript

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

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

    E assert 4 == 5 E + where 4 = double(2) sample_test.py:6: AssertionError
  3. Goal one Modify the assert statement to
 call a function

    with both sides 
 of the comparison
  4. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Goal two Write an import hook 
 that uses our

    transformer 
 to modify imported code
  19. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Summary This is probably a giant foot gun Corner cases

    left for the reader Python is awesome