Show More
@@ -200,6 +200,13 b' class InteractiveShell(SingletonConfigurable):' | |||
|
200 | 200 | |
|
201 | 201 | _instance = None |
|
202 | 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 | ) | |
|
209 | ||
|
203 | 210 | autocall = Enum((0,1,2), default_value=0, config=True, help= |
|
204 | 211 | """ |
|
205 | 212 | Make IPython automatically call any callable object even if you didn't |
@@ -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) |
@@ -2663,6 +2672,31 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2663 | 2672 | # Each cell is a *single* input, regardless of how many lines it has |
|
2664 | 2673 | self.execution_count += 1 |
|
2665 | 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 | ||
|
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 |
|
2668 | 2702 | interactivity parameter. |
@@ -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