##// END OF EJS Templates
DEV: AST transformers supplied to `InteractiveShell` can reject input....
Scott Sanderson -
Show More
@@ -54,6 +54,7 b' from IPython.core.profiledir import ProfileDir'
54 from IPython.core.prompts import PromptManager
54 from IPython.core.prompts import PromptManager
55 from IPython.core.usage import default_banner
55 from IPython.core.usage import default_banner
56 from IPython.lib.latextools import LaTeXTool
56 from IPython.lib.latextools import LaTeXTool
57 from IPython.lib.security import InputRejected
57 from IPython.testing.skipdoctest import skip_doctest
58 from IPython.testing.skipdoctest import skip_doctest
58 from IPython.utils import PyColorize
59 from IPython.utils import PyColorize
59 from IPython.utils import io
60 from IPython.utils import io
@@ -2786,7 +2787,13 b' class InteractiveShell(SingletonConfigurable):'
2786 return None
2787 return None
2787
2788
2788 # Apply AST transformations
2789 # Apply AST transformations
2789 code_ast = self.transform_ast(code_ast)
2790 try:
2791 code_ast = self.transform_ast(code_ast)
2792 except InputRejected:
2793 self.showtraceback()
2794 if store_history:
2795 self.execution_count += 1
2796 return None
2790
2797
2791 # Execute the user code
2798 # Execute the user code
2792 interactivity = "none" if silent else self.ast_node_interactivity
2799 interactivity = "none" if silent else self.ast_node_interactivity
@@ -2822,6 +2829,11 b' class InteractiveShell(SingletonConfigurable):'
2822 for transformer in self.ast_transformers:
2829 for transformer in self.ast_transformers:
2823 try:
2830 try:
2824 node = transformer.visit(node)
2831 node = transformer.visit(node)
2832 except InputRejected:
2833 # User-supplied AST transformers can reject an input by raising
2834 # an InputRejected. Short-circuit in this case so that we
2835 # don't unregister the transform.
2836 raise
2825 except Exception:
2837 except Exception:
2826 warn("AST transformer %r threw an error. It will be unregistered." % transformer)
2838 warn("AST transformer %r threw an error. It will be unregistered." % transformer)
2827 self.ast_transformers.remove(transformer)
2839 self.ast_transformers.remove(transformer)
@@ -25,6 +25,7 b' from os.path import join'
25 import nose.tools as nt
25 import nose.tools as nt
26
26
27 from IPython.core.inputtransformer import InputTransformer
27 from IPython.core.inputtransformer import InputTransformer
28 from IPython.lib.security import InputRejected
28 from IPython.testing.decorators import (
29 from IPython.testing.decorators import (
29 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
30 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
30 )
31 )
@@ -695,6 +696,41 b' class TestAstTransformError(unittest.TestCase):'
695 # This should have been removed.
696 # This should have been removed.
696 nt.assert_not_in(err_transformer, ip.ast_transformers)
697 nt.assert_not_in(err_transformer, ip.ast_transformers)
697
698
699
700 class StringRejector(ast.NodeTransformer):
701 """Throws an InputRejected when it sees a string literal.
702
703 Used to verify that NodeTransformers can signal that a piece of code should
704 not be executed by throwing an InputRejected.
705 """
706
707 def visit_Str(self, node):
708 raise InputRejected("test")
709
710
711 class TestAstTransformInputRejection(unittest.TestCase):
712
713 def setUp(self):
714 self.transformer = StringRejector()
715 ip.ast_transformers.append(self.transformer)
716
717 def tearDown(self):
718 ip.ast_transformers.remove(self.transformer)
719
720 def test_input_rejection(self):
721 """Check that NodeTransformers can reject input."""
722
723 expect_exception_tb = tt.AssertPrints("InputRejected: test")
724 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
725
726 # Run the same check twice to verify that the transformer is not
727 # disabled after raising.
728 with expect_exception_tb, expect_no_cell_output:
729 ip.run_cell("'unsafe'")
730
731 with expect_exception_tb, expect_no_cell_output:
732 ip.run_cell("'unsafe'")
733
698 def test__IPYTHON__():
734 def test__IPYTHON__():
699 # This shouldn't raise a NameError, that's all
735 # This shouldn't raise a NameError, that's all
700 __IPYTHON__
736 __IPYTHON__
@@ -114,3 +114,13 b' def passwd_check(hashed_passphrase, passphrase):'
114 h.update(cast_bytes(passphrase, 'utf-8') + cast_bytes(salt, 'ascii'))
114 h.update(cast_bytes(passphrase, 'utf-8') + cast_bytes(salt, 'ascii'))
115
115
116 return h.hexdigest() == pw_digest
116 return h.hexdigest() == pw_digest
117
118 #-----------------------------------------------------------------------------
119 # Exception classes
120 #-----------------------------------------------------------------------------
121 class InputRejected(Exception):
122 """Input rejected by ast transformer.
123
124 To be raised by user-supplied ast.NodeTransformers when an input should not
125 be executed.
126 """
General Comments 0
You need to be logged in to leave comments. Login now