Skip to content
This repository was archived by the owner on Nov 3, 2023. It is now read-only.

Commit e886115

Browse files
committed
Prevent AssertionError parsing __future__ import
The __future__ import parser introduced in 0.7.0 would cause pep257 to crash with an AssertionError for some obscure valid syntax, and many invalid syntax.
1 parent 20dc16f commit e886115

File tree

2 files changed

+137
-18
lines changed

2 files changed

+137
-18
lines changed

src/pep257.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,8 @@ def parse_from_import_statement(self):
492492
expected_end_kind = tk.OP
493493
else:
494494
expected_end_kind = tk.NEWLINE
495-
while self.current.kind != expected_end_kind:
495+
while self.current.kind != expected_end_kind and not(
496+
self.current.kind == tk.OP and self.current.value == ';'):
496497
if self.current.kind != tk.NAME:
497498
self.stream.move()
498499
continue
@@ -503,9 +504,10 @@ def parse_from_import_statement(self):
503504
self.consume(tk.NAME)
504505
log.debug("parsing import, token is %r (%s)",
505506
self.current.kind, self.current.value)
506-
if self.current.kind == tk.NAME:
507+
if self.current.kind == tk.NAME and self.current.value == 'as':
507508
self.consume(tk.NAME) # as
508-
self.consume(tk.NAME) # new name, irrelevant
509+
if self.current.kind == tk.NAME:
510+
self.consume(tk.NAME) # new name, irrelevant
509511
if self.current.value == ',':
510512
self.consume(tk.OP)
511513
log.debug("parsing import, token is %r (%s)",

src/tests/test_definitions.py

Lines changed: 132 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,91 @@ def nested_3(self):
3838
# Inconvenient comment.
3939
'a', 'b' 'c',]
4040
'''
41-
source_unicode_literals = '''
41+
source_unicode_literals1 = """
4242
from __future__ import unicode_literals
43-
'''
44-
source_multiple_future_imports = '''
43+
"""
44+
source_unicode_literals2 = """
45+
from __future__ import unicode_literals;
46+
"""
47+
source_unicode_literals3 = """
48+
from \
49+
__future__ \
50+
import \
51+
unicode_literals
52+
"""
53+
source_unicode_literals4 = """
54+
from __future__ \
55+
import unicode_literals \
56+
;
57+
"""
58+
source_unicode_literals5 = """
59+
from __future__ import unicode_literals as foo;
60+
"""
61+
source_unicode_literals6 = """
62+
from __future__ import unicode_literals as foo; import string
63+
"""
64+
source_multiple_future_imports1 = """
4565
from __future__ import (nested_scopes as ns,
4666
unicode_literals)
47-
'''
67+
"""
68+
source_multiple_future_imports2 = """
69+
from __future__ import nested_scopes as ns
70+
from __future__ import unicode_literals
71+
"""
72+
source_multiple_future_imports3 = """
73+
from __future__ import nested_scopes, unicode_literals
74+
"""
75+
source_multiple_future_imports4 = """
76+
from __future__ import (nested_scopes as ns, )
77+
from __future__ import (unicode_literals)
78+
"""
79+
source_multiple_future_imports5 = """
80+
from __future__ \
81+
import nested_scopes
82+
from __future__ \
83+
import unicode_literals
84+
"""
85+
source_multiple_future_imports6 = """
86+
from __future__ import nested_scopes; from __future__ import unicode_literals
87+
"""
88+
# pep257 does not detect that 'import string' prevents
89+
# unicode_literals from being a valid __future__.
90+
# That is detected by pyflakes.
91+
source_multiple_future_imports7 = """
92+
from __future__ import nested_scopes; import string; from __future__ import \
93+
unicode_literals
94+
"""
95+
96+
source_future_import_invalid1 = """
97+
from __future__ import unicode_literals as;
98+
"""
99+
source_future_import_invalid2 = """
100+
from __future__ import unicode_literals as \
101+
;
102+
"""
103+
source_future_import_invalid3 = """
104+
from __future__ import
105+
"""
106+
source_future_import_invalid4 = """
107+
from __future__ import \
108+
109+
"""
110+
source_future_import_invalid5 = """
111+
from __future__ import \
112+
;
113+
"""
114+
source_future_import_invalid6 = """
115+
from __future__ import \
116+
;
117+
"""
118+
source_future_import_invalid7 = """
119+
from __future__ import unicode_literals, (\
120+
nested_scopes)
121+
"""
122+
source_future_import_invalid8 = """
123+
from __future__ import (, )
124+
"""
125+
48126
source_complex_all = '''
49127
import foo
50128
import bar
@@ -95,21 +173,60 @@ def test_parser():
95173
len(source_alt_nl_at_bracket.split('\n')), _, None, _, _,
96174
dunder_all, {}) == module
97175

98-
module = parse(StringIO(source_unicode_literals), 'file_ucl.py')
99-
assert Module('file_ucl.py', _, 1,
100-
_, _, None, _, _,
101-
_, {'unicode_literals': True}) == module
102-
103-
module = parse(StringIO(source_multiple_future_imports), 'file_mfi.py')
104-
assert Module('file_mfi.py', _, 1,
105-
_, _, None, _, _,
106-
_, {'unicode_literals': True, 'nested_scopes': True}) \
107-
== module
108-
assert module.future_imports['unicode_literals']
109176
with pytest.raises(AllError):
110177
parse(StringIO(source_complex_all), 'file_complex_all.py')
111178

112179

180+
def test_import_parser():
181+
for i, source_ucl in enumerate((
182+
source_unicode_literals1,
183+
source_unicode_literals2,
184+
source_unicode_literals3,
185+
source_unicode_literals4,
186+
source_unicode_literals5,
187+
source_unicode_literals6,
188+
), 1):
189+
module = parse(StringIO(source_ucl), 'file_ucl{0}.py'.format(i))
190+
191+
assert Module('file_ucl{0}.py'.format(i), _, 1,
192+
_, _, None, _, _,
193+
_, {'unicode_literals': True}) == module
194+
assert module.future_imports['unicode_literals']
195+
196+
for i, source_mfi in enumerate((
197+
source_multiple_future_imports1,
198+
source_multiple_future_imports2,
199+
source_multiple_future_imports3,
200+
source_multiple_future_imports4,
201+
source_multiple_future_imports5,
202+
source_multiple_future_imports6,
203+
source_multiple_future_imports7,
204+
), 1):
205+
module = parse(StringIO(source_mfi), 'file_mfi{0}.py'.format(i))
206+
assert Module('file_mfi{0}.py'.format(i), _, 1,
207+
_, _, None, _, _,
208+
_, {'unicode_literals': True, 'nested_scopes': True}) \
209+
== module
210+
assert module.future_imports['unicode_literals']
211+
212+
# These are invalid syntax, so there is no need to verify the result
213+
for i, source_ucli in enumerate((
214+
source_future_import_invalid1,
215+
source_future_import_invalid2,
216+
source_future_import_invalid3,
217+
source_future_import_invalid4,
218+
source_future_import_invalid5,
219+
source_future_import_invalid6,
220+
source_future_import_invalid7,
221+
source_future_import_invalid8,
222+
), 1):
223+
module = parse(StringIO(source_ucl), 'file_invalid{0}.py'.format(i))
224+
225+
assert Module('file_invalid{0}.py'.format(i), _, 1,
226+
_, _, None, _, _,
227+
_, _) == module
228+
229+
113230
def _test_module():
114231

115232
module = Module(source, 'module.py')

0 commit comments

Comments
 (0)