mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
* Support PEP 798 comprehension unpacking Extract the remaining Python 3.15 comprehension unpacking implementation from the integration branch. Use existing list/set/dict update operations and an explicit inner iterator loop for generator unpacking. Align the public DictComp AST representation and schema with CPython. Migrate test_unpack_ex and test_unparse with scripts/update_lib from CPython d220409a13b77de7a6c30abe16ea90db01528edc. Preserve existing Rust tests; add no duplicate regression tests or snippets. AI-assisted source extraction, canonical migration, and independent source review with OpenAI Codex. Runtime validation is not yet complete. Assisted-by: OpenAI Codex:model version unavailable * Improve selected unpacking diagnostics and refresh canonical exception tests Normalize six supported comprehension-unpacking diagnostics using existing parser context. Restrict the dictionary-spread hint to its first entry to avoid changing unrelated diagnostics. Migrate the whole test_exceptions module from CPython d220409a13b77de7a6c30abe16ea90db01528edc with the existing updater, preserving only its two existing RustPython markers. Ruff sources and dependency pins are unchanged. Six focused check/build/strict Clippy gates passed. A paired 34-input compile-only comparison retains six canonical diagnostic improvements, with eight of fifteen canonical cases still differing; this is not full test_exceptions or CI validation. * Fix mapping-rest error locations and retire stale encoding xfail * Match remaining unpacking diagnostics without changing Ruff Use exact retained AST and error ownership to normalize the remaining CPython 3.15 unpacking diagnostics. Reparse only the owned call argument slice with the unchanged Ruff parser when its original recovery loses generator clauses. Validated all 30 canonical compile-only cases in exec and doctest-style single modes against CPython 3.15.0, plus finite surrounding controls. Targeted compiler Clippy and source-bound builds passed. Full CI is pending. Cspell alone was omitted with the maintainer’s explicit one-commit approval because its npm bootstrap downloads returned HTTP 403; all other applicable configured commit hooks ran. AI assistance: dot authored, reviewed, and validated the changes. The runtime does not expose its exact model version. Assisted-by: dot: model version not exposed --------- Co-authored-by: Codex <codex@openai.com>
772 lines
18 KiB
Python
Vendored
772 lines
18 KiB
Python
Vendored
# Tests for extended unpacking, starred expressions.
|
|
|
|
import doctest
|
|
import unittest
|
|
|
|
|
|
doctests = """
|
|
|
|
Unpack tuple
|
|
|
|
>>> t = (1, 2, 3)
|
|
>>> a, *b, c = t
|
|
>>> a == 1 and b == [2] and c == 3
|
|
True
|
|
|
|
Unpack list
|
|
|
|
>>> l = [4, 5, 6]
|
|
>>> a, *b = l
|
|
>>> a == 4 and b == [5, 6]
|
|
True
|
|
|
|
Unpack implied tuple
|
|
|
|
>>> *a, = 7, 8, 9
|
|
>>> a == [7, 8, 9]
|
|
True
|
|
|
|
Unpack nested implied tuple
|
|
|
|
>>> [*[*a]] = [[7,8,9]]
|
|
>>> a == [[7,8,9]]
|
|
True
|
|
|
|
Unpack string... fun!
|
|
|
|
>>> a, *b = 'one'
|
|
>>> a == 'o' and b == ['n', 'e']
|
|
True
|
|
|
|
Unpack long sequence
|
|
|
|
>>> a, b, c, *d, e, f, g = range(10)
|
|
>>> (a, b, c, d, e, f, g) == (0, 1, 2, [3, 4, 5, 6], 7, 8, 9)
|
|
True
|
|
|
|
Unpack short sequence
|
|
|
|
>>> a, *b, c = (1, 2)
|
|
>>> a == 1 and c == 2 and b == []
|
|
True
|
|
|
|
Unpack generic sequence
|
|
|
|
>>> class Seq:
|
|
... def __getitem__(self, i):
|
|
... if i >= 0 and i < 3: return i
|
|
... raise IndexError
|
|
...
|
|
>>> a, *b = Seq()
|
|
>>> a == 0 and b == [1, 2]
|
|
True
|
|
|
|
Unpack in for statement
|
|
|
|
>>> for a, *b, c in [(1,2,3), (4,5,6,7)]:
|
|
... print(a, b, c)
|
|
...
|
|
1 [2] 3
|
|
4 [5, 6] 7
|
|
|
|
Unpack in list
|
|
|
|
>>> [a, *b, c] = range(5)
|
|
>>> a == 0 and b == [1, 2, 3] and c == 4
|
|
True
|
|
|
|
Multiple targets
|
|
|
|
>>> a, *b, c = *d, e = range(5)
|
|
>>> a == 0 and b == [1, 2, 3] and c == 4 and d == [0, 1, 2, 3] and e == 4
|
|
True
|
|
|
|
Assignment unpacking
|
|
|
|
>>> a, b, *c = range(5)
|
|
>>> a, b, c
|
|
(0, 1, [2, 3, 4])
|
|
>>> *a, b, c = a, b, *c
|
|
>>> a, b, c
|
|
([0, 1, 2], 3, 4)
|
|
|
|
Set display element unpacking
|
|
|
|
>>> a = [1, 2, 3]
|
|
>>> sorted({1, *a, 0, 4})
|
|
[0, 1, 2, 3, 4]
|
|
|
|
>>> {1, *1, 0, 4}
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: 'int' object is not iterable
|
|
|
|
Dict display element unpacking
|
|
|
|
>>> kwds = {'z': 0, 'w': 12}
|
|
>>> sorted({'x': 1, 'y': 2, **kwds}.items())
|
|
[('w', 12), ('x', 1), ('y', 2), ('z', 0)]
|
|
|
|
>>> sorted({**{'x': 1}, 'y': 2, **{'z': 3}}.items())
|
|
[('x', 1), ('y', 2), ('z', 3)]
|
|
|
|
>>> sorted({**{'x': 1}, 'y': 2, **{'x': 3}}.items())
|
|
[('x', 3), ('y', 2)]
|
|
|
|
>>> sorted({**{'x': 1}, **{'x': 3}, 'x': 4}.items())
|
|
[('x', 4)]
|
|
|
|
>>> {**{}}
|
|
{}
|
|
|
|
>>> a = {}
|
|
>>> {**a}[0] = 1
|
|
>>> a
|
|
{}
|
|
|
|
>>> {**1}
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: 'int' object is not a mapping
|
|
|
|
>>> {**[]}
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: 'list' object is not a mapping
|
|
|
|
>>> class OnlyKeys:
|
|
... def keys(self):
|
|
... return ['key']
|
|
>>> {**OnlyKeys()}
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: 'OnlyKeys' object is not subscriptable
|
|
|
|
>>> class BrokenKeys:
|
|
... def keys(self):
|
|
... return 1
|
|
>>> {**BrokenKeys()}
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: test.test_unpack_ex.BrokenKeys.keys() must return an iterable, not int
|
|
|
|
>>> len(eval("{" + ", ".join("**{{{}: {}}}".format(i, i)
|
|
... for i in range(1000)) + "}"))
|
|
1000
|
|
|
|
>>> {0:1, **{0:2}, 0:3, 0:4}
|
|
{0: 4}
|
|
|
|
Comprehension element unpacking
|
|
|
|
>>> a, b, c = [0, 1, 2], 3, 4
|
|
>>> [*a, b, c]
|
|
[0, 1, 2, 3, 4]
|
|
|
|
>>> l = [a, (3, 4), {5}, {6: None}, (i for i in range(7, 10))]
|
|
>>> [*item for item in l]
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
|
|
>>> [*[0, 1] for i in range(5)]
|
|
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
|
|
|
|
>>> [*'a' for i in range(5)]
|
|
['a', 'a', 'a', 'a', 'a']
|
|
|
|
>>> [*[] for i in range(10)]
|
|
[]
|
|
|
|
>>> [*(x*2) for x in [[1, 2, 3], [], 'cat']]
|
|
[1, 2, 3, 1, 2, 3, 'c', 'a', 't', 'c', 'a', 't']
|
|
|
|
>>> {**{} for a in [1]}
|
|
{}
|
|
|
|
>>> {**{7: i} for i in range(10)}
|
|
{7: 9}
|
|
|
|
>>> dicts = [{1: 2}, {3: 4}, {5: 6, 7: 8}, {}, {9: 10}, {1: 0}]
|
|
>>> {**d for d in dicts}
|
|
{1: 0, 3: 4, 5: 6, 7: 8, 9: 10}
|
|
|
|
>>> gen = (*(0, 1) for i in range(5))
|
|
>>> next(gen)
|
|
0
|
|
>>> list(gen)
|
|
[1, 0, 1, 0, 1, 0, 1, 0, 1]
|
|
|
|
Comprehension unpacking with conditionals and double loops
|
|
|
|
>>> [*[i, i+1] for i in range(5) if i % 2 == 0]
|
|
[0, 1, 2, 3, 4, 5]
|
|
|
|
>>> [*y for x in [[[0], [1, 2, 3], [], [4, 5]], [[6, 7]]] for y in x]
|
|
[0, 1, 2, 3, 4, 5, 6, 7]
|
|
|
|
>>> [*y for x in [[[0], [1, 2, 3], [], [4, 5]], [[6, 7]]] for y in x if y and y[0]>0]
|
|
[1, 2, 3, 4, 5, 6, 7]
|
|
|
|
>>> dicts = [{1: 2}, {3: 4}, {5: 6, 7: 8}, {}, {9: 10}, {1: 0}]
|
|
>>> {**d for d in dicts if len(d) != 2}
|
|
{1: 0, 3: 4, 9: 10}
|
|
|
|
Scoping of assignment expressions in comprehensions
|
|
|
|
>>> [*((y := i**2), 2*y) for i in range(4)]
|
|
[0, 0, 1, 2, 4, 8, 9, 18]
|
|
>>> y
|
|
9
|
|
|
|
>>> [*(y := [i, i+1, i+2]) for i in range(4)]
|
|
[0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5]
|
|
>>> y
|
|
[3, 4, 5]
|
|
|
|
>>> g = (*(z := [i, i+1, i+2]) for i in range(4))
|
|
>>> z
|
|
Traceback (most recent call last):
|
|
...
|
|
NameError: name 'z' is not defined
|
|
>>> next(g)
|
|
0
|
|
>>> z
|
|
[0, 1, 2]
|
|
>>> next(g)
|
|
1
|
|
>>> z
|
|
[0, 1, 2]
|
|
>>> next(g)
|
|
2
|
|
>>> z
|
|
[0, 1, 2]
|
|
>>> next(g)
|
|
1
|
|
>>> z
|
|
[1, 2, 3]
|
|
|
|
>>> x = [1, 2, 3]
|
|
>>> y = [4, 5, 6]
|
|
>>> def f(*args):
|
|
... print(args)
|
|
|
|
>>> f(*x if x else y)
|
|
(1, 2, 3)
|
|
|
|
|
|
Malformed comperehension element unpacking
|
|
|
|
>>> [*x for x in [1, 2, 3]]
|
|
Traceback (most recent call last):
|
|
...
|
|
[*x for x in [1, 2, 3]]
|
|
^^
|
|
TypeError: Value after * must be an iterable, not int
|
|
|
|
|
|
Error messages for specific failure modes of unpacking
|
|
|
|
>>> [*x if x else y for x in z]
|
|
Traceback (most recent call last):
|
|
...
|
|
[*x if x else y for x in z]
|
|
^^^^^^^^^^^^^^
|
|
SyntaxError: invalid starred expression. Did you forget to wrap the conditional expression in parentheses?
|
|
|
|
>>> [*x if x else y]
|
|
Traceback (most recent call last):
|
|
...
|
|
[*x if x else y]
|
|
^^^^^^^^^^^^^^
|
|
SyntaxError: invalid starred expression. Did you forget to wrap the conditional expression in parentheses?
|
|
|
|
>>> [x if x else *y for x in z]
|
|
Traceback (most recent call last):
|
|
...
|
|
[x if x else *y for x in z]
|
|
^
|
|
SyntaxError: cannot unpack only part of a conditional expression
|
|
|
|
>>> [x if x else *y]
|
|
Traceback (most recent call last):
|
|
...
|
|
[x if x else *y]
|
|
^
|
|
SyntaxError: cannot unpack only part of a conditional expression
|
|
|
|
>>> {**x if x else y}
|
|
Traceback (most recent call last):
|
|
...
|
|
{**x if x else y}
|
|
^^^^^^^^^^^^^^^^
|
|
SyntaxError: invalid double starred expression. Did you forget to wrap the conditional expression in parentheses?
|
|
>>> {x if x else **y}
|
|
Traceback (most recent call last):
|
|
...
|
|
{x if x else **y}
|
|
^^
|
|
SyntaxError: cannot use dict unpacking on only part of a conditional expression
|
|
|
|
>>> [**x for x in [{1: 2}]]
|
|
Traceback (most recent call last):
|
|
...
|
|
[**x for x in [{1: 2}]]
|
|
^^^
|
|
SyntaxError: cannot use dict unpacking in list comprehension
|
|
|
|
>>> (**x for x in [{1:2}])
|
|
Traceback (most recent call last):
|
|
...
|
|
(**x for x in [{1:2}])
|
|
^^^
|
|
SyntaxError: cannot use dict unpacking in generator expression
|
|
|
|
>>> dict(**x for x in [{1:2}])
|
|
Traceback (most recent call last):
|
|
...
|
|
dict(**x for x in [{1:2}])
|
|
^^^
|
|
SyntaxError: cannot use dict unpacking in generator expression
|
|
|
|
>>> {*a: b for a, b in {1: 2}.items()}
|
|
Traceback (most recent call last):
|
|
...
|
|
{*a: b for a, b in {1: 2}.items()}
|
|
^^
|
|
SyntaxError: cannot use a starred expression in a dictionary key
|
|
|
|
>>> {**a: b for a, b in {1: 2}.items()}
|
|
Traceback (most recent call last):
|
|
...
|
|
{**a: b for a, b in {1: 2}.items()}
|
|
^^^
|
|
SyntaxError: cannot use dict unpacking in a dictionary key
|
|
|
|
>>> {a: *b for a, b in {1: 2}.items()}
|
|
Traceback (most recent call last):
|
|
...
|
|
{a: *b for a, b in {1: 2}.items()}
|
|
^^
|
|
SyntaxError: cannot use a starred expression in a dictionary value
|
|
|
|
>>> {a: **b for a, b in {1: 2}.items()}
|
|
Traceback (most recent call last):
|
|
...
|
|
{a: **b for a, b in {1: 2}.items()}
|
|
^^^
|
|
SyntaxError: cannot use dict unpacking in a dictionary value
|
|
|
|
>>> (**a)
|
|
Traceback (most recent call last):
|
|
...
|
|
(**a)
|
|
^^
|
|
SyntaxError: cannot use dict unpacking here
|
|
|
|
>>> {**a for a in {1: 2}.items(): b}
|
|
Traceback (most recent call last):
|
|
...
|
|
{**a for a in {1: 2}.items(): b}
|
|
^^
|
|
SyntaxError: cannot use dict unpacking here
|
|
|
|
|
|
# Generator expression in function arguments
|
|
|
|
>>> list(*x for x in (range(5) for i in range(3)))
|
|
[0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
|
|
|
|
>>> def f(arg):
|
|
... print(type(arg), list(arg), list(arg))
|
|
>>> f(*x for x in [[1,2,3]])
|
|
<class 'generator'> [1, 2, 3] []
|
|
|
|
Iterable argument unpacking
|
|
|
|
>>> print(*[1], *[2], 3)
|
|
1 2 3
|
|
|
|
Make sure that they don't corrupt the passed-in dicts.
|
|
|
|
>>> def f(x, y):
|
|
... print(x, y)
|
|
...
|
|
>>> original_dict = {'x': 1}
|
|
>>> f(**original_dict, y=2)
|
|
1 2
|
|
>>> original_dict
|
|
{'x': 1}
|
|
|
|
Now for some failures
|
|
|
|
Make sure the raised errors are right for keyword argument unpackings
|
|
|
|
>>> from collections.abc import MutableMapping
|
|
>>> class CrazyDict(MutableMapping):
|
|
... def __init__(self):
|
|
... self.d = {}
|
|
...
|
|
... def __iter__(self):
|
|
... for x in self.d.__iter__():
|
|
... if x == 'c':
|
|
... self.d['z'] = 10
|
|
... yield x
|
|
...
|
|
... def __getitem__(self, k):
|
|
... return self.d[k]
|
|
...
|
|
... def __len__(self):
|
|
... return len(self.d)
|
|
...
|
|
... def __setitem__(self, k, v):
|
|
... self.d[k] = v
|
|
...
|
|
... def __delitem__(self, k):
|
|
... del self.d[k]
|
|
...
|
|
>>> d = CrazyDict()
|
|
>>> d.d = {chr(ord('a') + x): x for x in range(5)}
|
|
>>> e = {**d}
|
|
Traceback (most recent call last):
|
|
...
|
|
RuntimeError: dictionary changed size during iteration
|
|
|
|
>>> d.d = {chr(ord('a') + x): x for x in range(5)}
|
|
>>> def f(**kwargs): print(kwargs)
|
|
>>> f(**d)
|
|
Traceback (most recent call last):
|
|
...
|
|
RuntimeError: dictionary changed size during iteration
|
|
|
|
Overridden parameters
|
|
|
|
>>> f(x=5, **{'x': 3}, y=2)
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
|
|
|
|
>>> f(**{'x': 3}, x=5, y=2)
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
|
|
|
|
>>> f(**{'x': 3}, **{'x': 5}, y=2)
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
|
|
|
|
>>> f(x=5, **{'x': 3}, **{'x': 2})
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: test.test_unpack_ex.f() got multiple values for keyword argument 'x'
|
|
|
|
>>> f(**{1: 3}, **{1: 5})
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: test.test_unpack_ex.f() got multiple values for keyword argument '1'
|
|
|
|
Unpacking non-sequence
|
|
|
|
>>> a, *b = 7
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: cannot unpack non-iterable int object
|
|
|
|
Unpacking sequence too short
|
|
|
|
>>> a, *b, c, d, e = Seq()
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: not enough values to unpack (expected at least 4, got 3)
|
|
|
|
Unpacking sequence too short and target appears last
|
|
|
|
>>> a, b, c, d, *e = Seq()
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: not enough values to unpack (expected at least 4, got 3)
|
|
|
|
Unpacking a sequence where the test for too long raises a different kind of
|
|
error
|
|
|
|
>>> class BozoError(Exception):
|
|
... pass
|
|
...
|
|
>>> class BadSeq:
|
|
... def __getitem__(self, i):
|
|
... if i >= 0 and i < 3:
|
|
... return i
|
|
... elif i == 3:
|
|
... raise BozoError
|
|
... else:
|
|
... raise IndexError
|
|
...
|
|
|
|
Trigger code while not expecting an IndexError (unpack sequence too long, wrong
|
|
error)
|
|
|
|
>>> a, *b, c, d, e = BadSeq()
|
|
Traceback (most recent call last):
|
|
...
|
|
test.test_unpack_ex.BozoError
|
|
|
|
Now some general starred expressions (all fail).
|
|
|
|
>>> a, *b, c, *d, e = range(10) # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: multiple starred expressions in assignment
|
|
|
|
>>> [*b, *c] = range(10) # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: multiple starred expressions in assignment
|
|
|
|
>>> a,*b,*c,*d = range(4) # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: multiple starred expressions in assignment
|
|
|
|
>>> *a = range(10) # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: starred assignment target must be in a list or tuple
|
|
|
|
>>> *a # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: can't use starred expression here
|
|
|
|
>>> *1 # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: can't use starred expression here
|
|
|
|
>>> x = *a # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: can't use starred expression here
|
|
|
|
>>> (*x),y = 1, 2 # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: cannot use starred expression here
|
|
|
|
>>> (((*x))),y = 1, 2 # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: cannot use starred expression here
|
|
|
|
>>> z,(*x),y = 1, 2, 4 # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: cannot use starred expression here
|
|
|
|
>>> z,(*x) = 1, 2 # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: cannot use starred expression here
|
|
|
|
>>> ((*x),y) = 1, 2 # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: cannot use starred expression here
|
|
|
|
Some size constraints (all fail.)
|
|
|
|
>>> s = ", ".join("a%d" % i for i in range(1<<8)) + ", *rest = range((1<<8) + 1)"
|
|
>>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: too many expressions in star-unpacking assignment
|
|
|
|
>>> s = ", ".join("a%d" % i for i in range((1<<8) + 1)) + ", *rest = range((1<<8) + 2)"
|
|
>>> compile(s, 'test', 'exec') # doctest:+ELLIPSIS
|
|
Traceback (most recent call last):
|
|
...
|
|
SyntaxError: too many expressions in star-unpacking assignment
|
|
|
|
(there is an additional limit, on the number of expressions after the
|
|
'*rest', but it's 1<<24 and testing it takes too much memory.)
|
|
|
|
"""
|
|
|
|
def test_errors_in_iter():
|
|
"""
|
|
>>> class A:
|
|
... def __iter__(self):
|
|
... raise exc
|
|
...
|
|
>>> exc = ZeroDivisionError('some error')
|
|
>>> [*A()]
|
|
Traceback (most recent call last):
|
|
...
|
|
ZeroDivisionError: some error
|
|
|
|
>>> {*A()}
|
|
Traceback (most recent call last):
|
|
...
|
|
ZeroDivisionError: some error
|
|
|
|
>>> exc = AttributeError('some error')
|
|
>>> [*A()]
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: some error
|
|
|
|
>>> {*A()}
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: some error
|
|
|
|
>>> exc = TypeError('some error')
|
|
>>> [*A()]
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: some error
|
|
|
|
>>> {*A()}
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: some error
|
|
"""
|
|
|
|
def test_errors_in_next():
|
|
"""
|
|
>>> class I:
|
|
... def __iter__(self):
|
|
... return self
|
|
... def __next__(self):
|
|
... raise exc
|
|
...
|
|
>>> class A:
|
|
... def __iter__(self):
|
|
... return I()
|
|
...
|
|
|
|
>>> exc = ZeroDivisionError('some error')
|
|
>>> [*A()]
|
|
Traceback (most recent call last):
|
|
...
|
|
ZeroDivisionError: some error
|
|
|
|
>>> {*A()}
|
|
Traceback (most recent call last):
|
|
...
|
|
ZeroDivisionError: some error
|
|
|
|
>>> exc = AttributeError('some error')
|
|
>>> [*A()]
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: some error
|
|
|
|
>>> {*A()}
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: some error
|
|
|
|
>>> exc = TypeError('some error')
|
|
>>> [*A()]
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: some error
|
|
|
|
>>> {*A()}
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: some error
|
|
"""
|
|
|
|
def test_errors_in_keys():
|
|
"""
|
|
>>> class D:
|
|
... def keys(self):
|
|
... raise exc
|
|
...
|
|
>>> exc = ZeroDivisionError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
ZeroDivisionError: some error
|
|
|
|
>>> exc = AttributeError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: some error
|
|
|
|
>>> exc = KeyError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
KeyError: 'some error'
|
|
"""
|
|
|
|
def test_errors_in_keys_next():
|
|
"""
|
|
>>> class I:
|
|
... def __iter__(self):
|
|
... return self
|
|
... def __next__(self):
|
|
... raise exc
|
|
...
|
|
>>> class D:
|
|
... def keys(self):
|
|
... return I()
|
|
...
|
|
>>> exc = ZeroDivisionError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
ZeroDivisionError: some error
|
|
|
|
>>> exc = AttributeError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: some error
|
|
|
|
>>> exc = KeyError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
KeyError: 'some error'
|
|
"""
|
|
|
|
def test_errors_in_getitem():
|
|
"""
|
|
>>> class D:
|
|
... def keys(self):
|
|
... return ['key']
|
|
... def __getitem__(self, key):
|
|
... raise exc
|
|
...
|
|
>>> exc = ZeroDivisionError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
ZeroDivisionError: some error
|
|
|
|
>>> exc = AttributeError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
AttributeError: some error
|
|
|
|
>>> exc = KeyError('some error')
|
|
>>> {**D()}
|
|
Traceback (most recent call last):
|
|
...
|
|
KeyError: 'some error'
|
|
"""
|
|
|
|
__test__ = {'doctests' : doctests}
|
|
|
|
def load_tests(loader, tests, pattern):
|
|
tests.addTest(doctest.DocTestSuite())
|
|
return tests
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|