Skip to content

gh-109485: Further improve test_future_stmt tests #109486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions Lib/test/test_future_stmt/badsyntax_future3.py

This file was deleted.

10 changes: 0 additions & 10 deletions Lib/test/test_future_stmt/badsyntax_future4.py

This file was deleted.

12 changes: 0 additions & 12 deletions Lib/test/test_future_stmt/badsyntax_future5.py

This file was deleted.

10 changes: 0 additions & 10 deletions Lib/test/test_future_stmt/badsyntax_future6.py

This file was deleted.

11 changes: 0 additions & 11 deletions Lib/test/test_future_stmt/badsyntax_future7.py

This file was deleted.

10 changes: 0 additions & 10 deletions Lib/test/test_future_stmt/badsyntax_future8.py

This file was deleted.

10 changes: 0 additions & 10 deletions Lib/test/test_future_stmt/badsyntax_future9.py

This file was deleted.

148 changes: 106 additions & 42 deletions Lib/test/test_future_stmt/test_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import re
import sys

TOP_LEVEL_MSG = 'from __future__ imports must occur at the beginning of the file'

rx = re.compile(r'\((\S+).py, line (\d+)')

def get_error_location(msg):
Expand All @@ -18,21 +20,48 @@ def get_error_location(msg):

class FutureTest(unittest.TestCase):

def check_syntax_error(self, err, basename, lineno, offset=1):
self.assertIn('%s.py, line %d' % (basename, lineno), str(err))
self.assertEqual(os.path.basename(err.filename), basename + '.py')
def check_syntax_error(self, err, basename,
*,
lineno,
message=TOP_LEVEL_MSG, offset=1):
if basename != '<string>':
basename += '.py'

self.assertEqual(f'{message} ({basename}, line {lineno})', str(err))
self.assertEqual(os.path.basename(err.filename), basename)
self.assertEqual(err.lineno, lineno)
self.assertEqual(err.offset, offset)

def test_future1(self):
with import_helper.CleanImport('test.test_future_stmt.future_test1'):
from test.test_future_stmt import future_test1
self.assertEqual(future_test1.result, 6)
def assertSyntaxError(self, code,
*,
lineno=1,
message=TOP_LEVEL_MSG, offset=1,
parametrize_docstring=True):
code = dedent(code.lstrip('\n'))
for add_docstring in ([False, True] if parametrize_docstring else [False]):
with self.subTest(code=code, add_docstring=add_docstring):
if add_docstring:
code = '"""Docstring"""\n' + code
lineno += 1
with self.assertRaises(SyntaxError) as cm:
exec(code)
self.check_syntax_error(cm.exception, "<string>",
lineno=lineno,
message=message,
offset=offset)

def test_import_nested_scope_twice(self):
# Import the name nested_scopes twice to trigger SF bug #407394
with import_helper.CleanImport(
'test.test_future_stmt.import_nested_scope_twice',
):
from test.test_future_stmt import import_nested_scope_twice
self.assertEqual(import_nested_scope_twice.result, 6)

def test_future2(self):
with import_helper.CleanImport('test.test_future_stmt.future_test2'):
from test.test_future_stmt import future_test2
self.assertEqual(future_test2.result, 6)
def test_nested_scope(self):
with import_helper.CleanImport('test.test_future_stmt.nested_scope'):
from test.test_future_stmt import nested_scope
self.assertEqual(nested_scope.result, 6)

def test_future_single_import(self):
with import_helper.CleanImport(
Expand All @@ -52,45 +81,80 @@ def test_future_multiple_features(self):
):
from test.test_future_stmt import test_future_multiple_features

def test_badfuture3(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future3
self.check_syntax_error(cm.exception, "badsyntax_future3", 3)
def test_unknown_future_flag(self):
code = """
from __future__ import nested_scopes
from __future__ import rested_snopes # typo error here: nested => rested
"""
self.assertSyntaxError(
code, lineno=2,
message='future feature rested_snopes is not defined',
)

def test_badfuture4(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future4
self.check_syntax_error(cm.exception, "badsyntax_future4", 3)
def test_future_import_not_on_top(self):
code = """
import some_module
from __future__ import annotations
"""
self.assertSyntaxError(code, lineno=2)

def test_badfuture5(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future5
self.check_syntax_error(cm.exception, "badsyntax_future5", 4)
code = """
import __future__
from __future__ import annotations
"""
self.assertSyntaxError(code, lineno=2)

def test_badfuture6(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future6
self.check_syntax_error(cm.exception, "badsyntax_future6", 3)
code = """
from __future__ import absolute_import
"spam, bar, blah"
from __future__ import print_function
"""
self.assertSyntaxError(code, lineno=3)

def test_badfuture7(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future7
self.check_syntax_error(cm.exception, "badsyntax_future7", 3, 54)
def test_future_import_with_extra_string(self):
code = """
'''Docstring'''
"this isn't a doc string"
from __future__ import nested_scopes
"""
self.assertSyntaxError(code, lineno=3, parametrize_docstring=False)

def test_badfuture8(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future8
self.check_syntax_error(cm.exception, "badsyntax_future8", 3)
def test_multiple_import_statements_on_same_line(self):
# With `\`:
code = """
from __future__ import nested_scopes; import string; from __future__ import \
nested_scopes
"""
self.assertSyntaxError(code, offset=54)

def test_badfuture9(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future9
self.check_syntax_error(cm.exception, "badsyntax_future9", 3)
# Without `\`:
code = """
from __future__ import nested_scopes; import string; from __future__ import nested_scopes
"""
self.assertSyntaxError(code, offset=54)

def test_future_import_star(self):
code = """
from __future__ import *
"""
self.assertSyntaxError(code, message='future feature * is not defined')

def test_future_import_braces(self):
code = """
from __future__ import braces
"""
# Congrats, you found an easter egg!
self.assertSyntaxError(code, message='not a chance')

code = """
from __future__ import nested_scopes, braces
"""
self.assertSyntaxError(code, message='not a chance')

def test_badfuture10(self):
def test_module_with_future_import_not_on_top(self):
with self.assertRaises(SyntaxError) as cm:
from test.test_future_stmt import badsyntax_future10
self.check_syntax_error(cm.exception, "badsyntax_future10", 3)
from test.test_future_stmt import badsyntax_future
self.check_syntax_error(cm.exception, "badsyntax_future", lineno=3)

def test_ensure_flags_dont_clash(self):
# bpo-39562: test that future flags and compiler flags doesn't clash
Expand Down