##// END OF EJS Templates
Add framework for AST transformations of input code.
Thomas Kluyver -
Show More
@@ -199,6 +199,13 b' class InteractiveShell(SingletonConfigurable):'
199 199 """An enhanced, interactive shell for Python."""
200 200
201 201 _instance = None
202
203 ast_transformers = List([], config=True, help=
204 """
205 A list of ast.NodeTransformer subclass instances, which will be applied
206 to user input before code is run.
207 """
208 )
202 209
203 210 autocall = Enum((0,1,2), default_value=0, config=True, help=
204 211 """
@@ -2630,6 +2637,8 b' class InteractiveShell(SingletonConfigurable):'
2630 2637 self.execution_count += 1
2631 2638 return None
2632 2639
2640 code_ast = self.transform_ast(code_ast)
2641
2633 2642 interactivity = "none" if silent else self.ast_node_interactivity
2634 2643 self.run_ast_nodes(code_ast.body, cell_name,
2635 2644 interactivity=interactivity)
@@ -2662,6 +2671,31 b' class InteractiveShell(SingletonConfigurable):'
2662 2671 self.history_manager.store_output(self.execution_count)
2663 2672 # Each cell is a *single* input, regardless of how many lines it has
2664 2673 self.execution_count += 1
2674
2675 def transform_ast(self, node):
2676 """Apply the AST transformations from self.ast_transformers
2677
2678 Parameters
2679 ----------
2680 node : ast.Node
2681 The root node to be transformed. Typically called with the ast.Module
2682 produced by parsing user input.
2683
2684 Returns
2685 -------
2686 An ast.Node corresponding to the node it was called with. Note that it
2687 may also modify the passed object, so don't rely on references to the
2688 original AST.
2689 """
2690 for transformer in self.ast_transformers:
2691 try:
2692 node = transformer.visit(node)
2693 except Exception:
2694 warn("AST transformer %r threw an error. It will be unregistered.")
2695 self.ast_transformers.remove(transformer)
2696
2697 return node
2698
2665 2699
2666 2700 def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
2667 2701 """Run a sequence of AST nodes. The execution mode depends on the
@@ -20,6 +20,7 b' Authors'
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22 # stdlib
23 import ast
23 24 import os
24 25 import shutil
25 26 import sys
@@ -414,6 +415,28 b' class TestModules(unittest.TestCase, tt.TempFileMixin):'
414 415 out = "False\nFalse\nFalse\n"
415 416 tt.ipexec_validate(self.fname, out)
416 417
418 class Negator(ast.NodeTransformer):
419 """Negates all number literals in an AST."""
420 def visit_Num(self, node):
421 node.n = -node.n
422 return node
423
424 class TestAstTransform(unittest.TestCase):
425 def setUp(self):
426 self.negator = Negator()
427 ip.ast_transformers.append(self.negator)
428
429 def tearDown(self):
430 ip.ast_transformers.remove(self.negator)
431
432 def test_run_cell(self):
433 with tt.AssertPrints('-34'):
434 ip.run_cell('print (12 + 22)')
435
436 # A named reference to a number shouldn't be transformed.
437 ip.user_ns['n'] = 55
438 with tt.AssertNotPrints('-55'):
439 ip.run_cell('print (n)')
417 440
418 441 def test__IPYTHON__():
419 442 # This shouldn't raise a NameError, that's all
General Comments 0
You need to be logged in to leave comments. Login now