Show More
@@ -199,6 +199,13 b' class InteractiveShell(SingletonConfigurable):' | |||||
199 | """An enhanced, interactive shell for Python.""" |
|
199 | """An enhanced, interactive shell for Python.""" | |
200 |
|
200 | |||
201 | _instance = None |
|
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 | autocall = Enum((0,1,2), default_value=0, config=True, help= |
|
210 | autocall = Enum((0,1,2), default_value=0, config=True, help= | |
204 | """ |
|
211 | """ | |
@@ -2630,6 +2637,8 b' class InteractiveShell(SingletonConfigurable):' | |||||
2630 | self.execution_count += 1 |
|
2637 | self.execution_count += 1 | |
2631 | return None |
|
2638 | return None | |
2632 |
|
2639 | |||
|
2640 | code_ast = self.transform_ast(code_ast) | |||
|
2641 | ||||
2633 | interactivity = "none" if silent else self.ast_node_interactivity |
|
2642 | interactivity = "none" if silent else self.ast_node_interactivity | |
2634 | self.run_ast_nodes(code_ast.body, cell_name, |
|
2643 | self.run_ast_nodes(code_ast.body, cell_name, | |
2635 | interactivity=interactivity) |
|
2644 | interactivity=interactivity) | |
@@ -2662,6 +2671,31 b' class InteractiveShell(SingletonConfigurable):' | |||||
2662 | self.history_manager.store_output(self.execution_count) |
|
2671 | self.history_manager.store_output(self.execution_count) | |
2663 | # Each cell is a *single* input, regardless of how many lines it has |
|
2672 | # Each cell is a *single* input, regardless of how many lines it has | |
2664 | self.execution_count += 1 |
|
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 | def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'): |
|
2700 | def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'): | |
2667 | """Run a sequence of AST nodes. The execution mode depends on the |
|
2701 | """Run a sequence of AST nodes. The execution mode depends on the |
@@ -20,6 +20,7 b' Authors' | |||||
20 | # Imports |
|
20 | # Imports | |
21 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
22 | # stdlib |
|
22 | # stdlib | |
|
23 | import ast | |||
23 | import os |
|
24 | import os | |
24 | import shutil |
|
25 | import shutil | |
25 | import sys |
|
26 | import sys | |
@@ -414,6 +415,28 b' class TestModules(unittest.TestCase, tt.TempFileMixin):' | |||||
414 | out = "False\nFalse\nFalse\n" |
|
415 | out = "False\nFalse\nFalse\n" | |
415 | tt.ipexec_validate(self.fname, out) |
|
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 | def test__IPYTHON__(): |
|
441 | def test__IPYTHON__(): | |
419 | # This shouldn't raise a NameError, that's all |
|
442 | # This shouldn't raise a NameError, that's all |
General Comments 0
You need to be logged in to leave comments.
Login now