Skip to content
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ env:
test_math
test_operator
test_ordered_dict
test_pep646_syntax
test_pow
test_raise
test_richcmp
Expand Down
338 changes: 338 additions & 0 deletions Lib/test/test_pep646_syntax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
import doctest
import unittest

doctests = """

Setup

>>> class AClass:
... def __init__(self):
... self._setitem_name = None
... self._setitem_val = None
... self._delitem_name = None
... def __setitem__(self, name, val):
... self._delitem_name = None
... self._setitem_name = name
... self._setitem_val = val
... def __repr__(self):
... if self._setitem_name is not None:
... return f"A[{self._setitem_name}]={self._setitem_val}"
... elif self._delitem_name is not None:
... return f"delA[{self._delitem_name}]"
... def __getitem__(self, name):
... return ParameterisedA(name)
... def __delitem__(self, name):
... self._setitem_name = None
... self._delitem_name = name
...
>>> class ParameterisedA:
... def __init__(self, name):
... self._name = name
... def __repr__(self):
... return f"A[{self._name}]"
... def __iter__(self):
... for p in self._name:
... yield p
>>> class B:
... def __iter__(self):
... yield StarredB()
... def __repr__(self):
... return "B"
>>> class StarredB:
... def __repr__(self):
... return "StarredB"
>>> A = AClass()
>>> b = B()

Slices that are supposed to work, starring our custom B class

>>> A[*b]
A[(StarredB,)]
>>> A[*b] = 1; A
A[(StarredB,)]=1
>>> del A[*b]; A
delA[(StarredB,)]

>>> A[*b, *b]
A[(StarredB, StarredB)]
>>> A[*b, *b] = 1; A
A[(StarredB, StarredB)]=1
>>> del A[*b, *b]; A
delA[(StarredB, StarredB)]

>>> A[b, *b]
A[(B, StarredB)]
>>> A[b, *b] = 1; A
A[(B, StarredB)]=1
>>> del A[b, *b]; A
delA[(B, StarredB)]

>>> A[*b, b]
A[(StarredB, B)]
>>> A[*b, b] = 1; A
A[(StarredB, B)]=1
>>> del A[*b, b]; A
delA[(StarredB, B)]

>>> A[b, b, *b]
A[(B, B, StarredB)]
>>> A[b, b, *b] = 1; A
A[(B, B, StarredB)]=1
>>> del A[b, b, *b]; A
delA[(B, B, StarredB)]

>>> A[*b, b, b]
A[(StarredB, B, B)]
>>> A[*b, b, b] = 1; A
A[(StarredB, B, B)]=1
>>> del A[*b, b, b]; A
delA[(StarredB, B, B)]

>>> A[b, *b, b]
A[(B, StarredB, B)]
>>> A[b, *b, b] = 1; A
A[(B, StarredB, B)]=1
>>> del A[b, *b, b]; A
delA[(B, StarredB, B)]

>>> A[b, b, *b, b]
A[(B, B, StarredB, B)]
>>> A[b, b, *b, b] = 1; A
A[(B, B, StarredB, B)]=1
>>> del A[b, b, *b, b]; A
delA[(B, B, StarredB, B)]

>>> A[b, *b, b, b]
A[(B, StarredB, B, B)]
>>> A[b, *b, b, b] = 1; A
A[(B, StarredB, B, B)]=1
>>> del A[b, *b, b, b]; A
delA[(B, StarredB, B, B)]

>>> A[A[b, *b, b]]
A[A[(B, StarredB, B)]]
>>> A[A[b, *b, b]] = 1; A
A[A[(B, StarredB, B)]]=1
>>> del A[A[b, *b, b]]; A
delA[A[(B, StarredB, B)]]

>>> A[*A[b, *b, b]]
A[(B, StarredB, B)]
>>> A[*A[b, *b, b]] = 1; A
A[(B, StarredB, B)]=1
>>> del A[*A[b, *b, b]]; A
delA[(B, StarredB, B)]

>>> A[b, ...]
A[(B, Ellipsis)]
>>> A[b, ...] = 1; A
A[(B, Ellipsis)]=1
>>> del A[b, ...]; A
delA[(B, Ellipsis)]

>>> A[*A[b, ...]]
A[(B, Ellipsis)]
>>> A[*A[b, ...]] = 1; A
A[(B, Ellipsis)]=1
>>> del A[*A[b, ...]]; A
delA[(B, Ellipsis)]

Slices that are supposed to work, starring a list

>>> l = [1, 2, 3]

>>> A[*l]
A[(1, 2, 3)]
>>> A[*l] = 1; A
A[(1, 2, 3)]=1
>>> del A[*l]; A
delA[(1, 2, 3)]

>>> A[*l, 4]
A[(1, 2, 3, 4)]
>>> A[*l, 4] = 1; A
A[(1, 2, 3, 4)]=1
>>> del A[*l, 4]; A
delA[(1, 2, 3, 4)]

>>> A[0, *l]
A[(0, 1, 2, 3)]
>>> A[0, *l] = 1; A
A[(0, 1, 2, 3)]=1
>>> del A[0, *l]; A
delA[(0, 1, 2, 3)]

>>> A[1:2, *l]
A[(slice(1, 2, None), 1, 2, 3)]
>>> A[1:2, *l] = 1; A
A[(slice(1, 2, None), 1, 2, 3)]=1
>>> del A[1:2, *l]; A
delA[(slice(1, 2, None), 1, 2, 3)]

>>> repr(A[1:2, *l]) == repr(A[1:2, 1, 2, 3])
True

Slices that are supposed to work, starring a tuple

>>> t = (1, 2, 3)

>>> A[*t]
A[(1, 2, 3)]
>>> A[*t] = 1; A
A[(1, 2, 3)]=1
>>> del A[*t]; A
delA[(1, 2, 3)]

>>> A[*t, 4]
A[(1, 2, 3, 4)]
>>> A[*t, 4] = 1; A
A[(1, 2, 3, 4)]=1
>>> del A[*t, 4]; A
delA[(1, 2, 3, 4)]

>>> A[0, *t]
A[(0, 1, 2, 3)]
>>> A[0, *t] = 1; A
A[(0, 1, 2, 3)]=1
>>> del A[0, *t]; A
delA[(0, 1, 2, 3)]

>>> A[1:2, *t]
A[(slice(1, 2, None), 1, 2, 3)]
>>> A[1:2, *t] = 1; A
A[(slice(1, 2, None), 1, 2, 3)]=1
>>> del A[1:2, *t]; A
delA[(slice(1, 2, None), 1, 2, 3)]

>>> repr(A[1:2, *t]) == repr(A[1:2, 1, 2, 3])
True

Starring an expression (rather than a name) in a slice

>>> def returns_list():
... return [1, 2, 3]

>>> A[returns_list()]
A[[1, 2, 3]]
>>> A[returns_list()] = 1; A
A[[1, 2, 3]]=1
>>> del A[returns_list()]; A
delA[[1, 2, 3]]

>>> A[returns_list(), 4]
A[([1, 2, 3], 4)]
>>> A[returns_list(), 4] = 1; A
A[([1, 2, 3], 4)]=1
>>> del A[returns_list(), 4]; A
delA[([1, 2, 3], 4)]

>>> A[*returns_list()]
A[(1, 2, 3)]
>>> A[*returns_list()] = 1; A
A[(1, 2, 3)]=1
>>> del A[*returns_list()]; A
delA[(1, 2, 3)]

>>> A[*returns_list(), 4]
A[(1, 2, 3, 4)]
>>> A[*returns_list(), 4] = 1; A
A[(1, 2, 3, 4)]=1
>>> del A[*returns_list(), 4]; A
delA[(1, 2, 3, 4)]

>>> A[0, *returns_list()]
A[(0, 1, 2, 3)]
>>> A[0, *returns_list()] = 1; A
A[(0, 1, 2, 3)]=1
>>> del A[0, *returns_list()]; A
delA[(0, 1, 2, 3)]

>>> A[*returns_list(), *returns_list()]
A[(1, 2, 3, 1, 2, 3)]
>>> A[*returns_list(), *returns_list()] = 1; A
A[(1, 2, 3, 1, 2, 3)]=1
>>> del A[*returns_list(), *returns_list()]; A
delA[(1, 2, 3, 1, 2, 3)]

Using both a starred object and a start:stop in a slice
(See also tests in test_syntax confirming that starring *inside* a start:stop
is *not* valid syntax.)

>>> A[1:2, *b]
A[(slice(1, 2, None), StarredB)]
>>> A[*b, 1:2]
A[(StarredB, slice(1, 2, None))]
>>> A[1:2, *b, 1:2]
A[(slice(1, 2, None), StarredB, slice(1, 2, None))]
>>> A[*b, 1:2, *b]
A[(StarredB, slice(1, 2, None), StarredB)]

>>> A[1:, *b]
A[(slice(1, None, None), StarredB)]
>>> A[*b, 1:]
A[(StarredB, slice(1, None, None))]
>>> A[1:, *b, 1:]
A[(slice(1, None, None), StarredB, slice(1, None, None))]
>>> A[*b, 1:, *b]
A[(StarredB, slice(1, None, None), StarredB)]

>>> A[:1, *b]
A[(slice(None, 1, None), StarredB)]
>>> A[*b, :1]
A[(StarredB, slice(None, 1, None))]
>>> A[:1, *b, :1]
A[(slice(None, 1, None), StarredB, slice(None, 1, None))]
>>> A[*b, :1, *b]
A[(StarredB, slice(None, 1, None), StarredB)]

>>> A[:, *b]
A[(slice(None, None, None), StarredB)]
>>> A[*b, :]
A[(StarredB, slice(None, None, None))]
>>> A[:, *b, :]
A[(slice(None, None, None), StarredB, slice(None, None, None))]
>>> A[*b, :, *b]
A[(StarredB, slice(None, None, None), StarredB)]

*args annotated as starred expression

>>> def f1(*args: *b): pass
>>> f1.__annotations__
{'args': StarredB}

>>> def f2(*args: *b, arg1): pass
>>> f2.__annotations__
{'args': StarredB}

>>> def f3(*args: *b, arg1: int): pass
>>> f3.__annotations__ # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE
{'args': StarredB, 'arg1': <class 'int'>}

>>> def f4(*args: *b, arg1: int = 2): pass
>>> f4.__annotations__ # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE
{'args': StarredB, 'arg1': <class 'int'>}

>>> def f5(*args: *b = (1,)): pass # TODO: RUSTPYTHON # doctest: +EXPECTED_FAILURE
Traceback (most recent call last):
...
SyntaxError: invalid syntax
"""

__test__ = {'doctests' : doctests}

EXPECTED_FAILURE = doctest.register_optionflag('EXPECTED_FAILURE') # TODO: RUSTPYTHON
class CustomOutputChecker(doctest.OutputChecker): # TODO: RUSTPYTHON
def check_output(self, want, got, optionflags): # TODO: RUSTPYTHON
if optionflags & EXPECTED_FAILURE: # TODO: RUSTPYTHON
if want == got: # TODO: RUSTPYTHON
return False # TODO: RUSTPYTHON
return True # TODO: RUSTPYTHON
return super().check_output(want, got, optionflags) # TODO: RUSTPYTHON

def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite(checker=CustomOutputChecker())) # TODO: RUSTPYTHON
return tests


if __name__ == "__main__":
unittest.main()
Loading