From 542cc5a499d86a23eb928fc3cadf2682aae74de5 2019-02-18 20:10:56 From: Matthias Bussonnier Date: 2019-02-18 20:10:56 Subject: [PATCH] Merge pull request #11593 from Carreau/ast-38 Mock the new module API on <38 and ignore second argument. --- diff --git a/.travis.yml b/.travis.yml index 9ad7b9b..75cf14f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,6 +67,9 @@ matrix: - python: "3.7" dist: xenial sudo: true + - python: "3.8-dev" + dist: xenial + sudo: true - python: "3.7-dev" dist: xenial sudo: true diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index a6dd344..3584efb 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -107,6 +107,14 @@ class ProvisionalWarning(DeprecationWarning): """ pass +if sys.version_info > (3,8): + from ast import Module +else : + # mock the new API, ignore second argument + # see https://github.com/ipython/ipython/issues/11590 + from ast import Module as OriginalModule + Module = lambda nodelist, type_ignores: OriginalModule(nodelist) + if sys.version_info > (3,6): _assign_nodes = (ast.AugAssign, ast.AnnAssign, ast.Assign) _single_targets_nodes = (ast.AugAssign, ast.AnnAssign) @@ -3188,15 +3196,15 @@ class InteractiveShell(SingletonConfigurable): if _async: # If interactivity is async the semantics of run_code are # completely different Skip usual machinery. - mod = ast.Module(nodelist) - async_wrapper_code = compiler(mod, 'cell_name', 'exec') + mod = Module(nodelist, []) + async_wrapper_code = compiler(mod, cell_name, 'exec') exec(async_wrapper_code, self.user_global_ns, self.user_ns) async_code = removed_co_newlocals(self.user_ns.pop('async-def-wrapper')).__code__ if (yield from self.run_code(async_code, result, async_=True)): return True else: for i, node in enumerate(to_run_exec): - mod = ast.Module([node]) + mod = Module([node], []) code = compiler(mod, cell_name, "exec") if (yield from self.run_code(code, result)): return True diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 26dbd0d..1c05653 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -609,10 +609,18 @@ class TestModules(tt.TempFileMixin, unittest.TestCase): class Negator(ast.NodeTransformer): """Negates all number literals in an AST.""" + + # for python 3.7 and earlier def visit_Num(self, node): node.n = -node.n return node + # for python 3.8+ + def visit_Constant(self, node): + if isinstance(node.value, int): + return self.visit_Num(node) + return node + class TestAstTransform(unittest.TestCase): def setUp(self): self.negator = Negator() @@ -674,12 +682,23 @@ class TestAstTransform(unittest.TestCase): class IntegerWrapper(ast.NodeTransformer): """Wraps all integers in a call to Integer()""" + + # for Python 3.7 and earlier + + # for Python 3.7 and earlier def visit_Num(self, node): if isinstance(node.n, int): return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()), args=[node], keywords=[]) return node + # For Python 3.8+ + def visit_Constant(self, node): + if isinstance(node.value, int): + return self.visit_Num(node) + return node + + class TestAstTransform2(unittest.TestCase): def setUp(self): self.intwrapper = IntegerWrapper() @@ -720,9 +739,18 @@ class TestAstTransform2(unittest.TestCase): class ErrorTransformer(ast.NodeTransformer): """Throws an error when it sees a number.""" + + # for Python 3.7 and earlier def visit_Num(self, node): raise ValueError("test") + # for Python 3.8+ + def visit_Constant(self, node): + if isinstance(node.value, int): + return self.visit_Num(node) + return node + + class TestAstTransformError(unittest.TestCase): def test_unregistering(self): err_transformer = ErrorTransformer() @@ -741,10 +769,17 @@ class StringRejector(ast.NodeTransformer): Used to verify that NodeTransformers can signal that a piece of code should not be executed by throwing an InputRejected. """ - + + #for python 3.7 and earlier def visit_Str(self, node): raise InputRejected("test") + # 3.8 only + def visit_Constant(self, node): + if isinstance(node.value, str): + raise InputRejected("test") + return node + class TestAstTransformInputRejection(unittest.TestCase): diff --git a/IPython/core/tests/test_ultratb.py b/IPython/core/tests/test_ultratb.py index 3fea1d4..536a4db 100644 --- a/IPython/core/tests/test_ultratb.py +++ b/IPython/core/tests/test_ultratb.py @@ -379,10 +379,16 @@ def test_handlers(): handler(*sys.exc_info()) buff.write('') +from IPython.testing.decorators import skipif class TokenizeFailureTest(unittest.TestCase): """Tests related to https://github.com/ipython/ipython/issues/6864.""" + # that appear to test that we are handling an exception that can be thrown + # by the tokenizer due to a bug that seem to have been fixed in 3.8, though + # I'm unsure if other sequences can make it raise this error. Let's just + # skip in 3.8 for now + @skipif(sys.version_info > (3,8)) def testLogging(self): message = "An unexpected error occurred while tokenizing input" cell = 'raise ValueError("""a\nb""")' diff --git a/IPython/terminal/ptutils.py b/IPython/terminal/ptutils.py index bc22f8e..4f21cb0 100644 --- a/IPython/terminal/ptutils.py +++ b/IPython/terminal/ptutils.py @@ -53,7 +53,7 @@ def _elide(string, *, min_elide=30): def _adjust_completion_text_based_on_context(text, body, offset): - if text.endswith('=') and len(body) > offset and body[offset] is '=': + if text.endswith('=') and len(body) > offset and body[offset] == '=': return text[:-1] else: return text