# mode: run
# tag: pep448
from __future__ import print_function
import sys
IS_PY3 = sys.version_info[0] >= 3
if IS_PY3:
__doc__ = """
>>> def f(*, w): pass
>>> try: errors_call_no_args(f)
... except TypeError: pass
... else: print("FAILED!")
>>> def f(*, a, b, c, d, e): pass
>>> try: errors_call_no_args(f)
... except TypeError: pass
... else: print("FAILED!")
>>> def f(*, kw, b): pass
>>> try: errors_call_3args_2kwargs(f)
... except TypeError: pass
... else: print("FAILED!")
>>> def f(a, b=2, *, kw): pass
>>> try: errors_call_3args_1kwarg(f)
... except TypeError: pass
... else: print("FAILED!")
>>> def f(*, kw): pass
>>> try: errors_call_1arg_1kwarg(f)
... except TypeError: pass
... else: print("FAILED!")
"""
# test for method/function calls. adapted from CPython's "test_extcall.py".
def sortdict(d):
return '{%s}' % ', '.join(['%r: %r' % item for item in sorted(d.items())])
# We're going the use these types for extra testing
try:
from collections import UserList, UserDict
except ImportError:
from UserList import UserList
from UserDict import UserDict
# We're defining four helper functions
def e(a,b):
print(a, b)
def f(*a, **k):
print(a, sortdict(k))
def g(x, *y, **z):
print(x, y, sortdict(z))
def h(j=1, a=2, h=3):
print(j, a, h)
# Argument list examples
def call_f_positional():
"""
>>> call_f_positional()
() {}
(1,) {}
(1, 2) {}
(1, 2, 3) {}
(1, 2, 3, 4, 5) {}
(1, 2, 3, 4, 5) {}
(1, 2, 3, 4, 5) {}
(1, 2, 3, 4, 5) {}
(1, 2, 3, 4, 5, 6, 7) {}
(1, 2, 3, 4, 5, 6, 7) {}
(1, 2, 3, 4, 5, 6, 7) {}
(1, 2) {}
"""
f()
f(1)
f(1, 2)
f(1, 2, 3)
f(1, 2, 3, *(4, 5))
f(1, 2, 3, *[4, 5])
f(*[1, 2, 3], 4, 5)
f(1, 2, 3, *UserList([4, 5]))
f(1, 2, 3, *[4, 5], *[6, 7])
f(1, *[2, 3], 4, *[5, 6], 7)
f(*UserList([1, 2]), *UserList([3, 4]), 5, *UserList([6, 7]))
f(1, *[] or () and {}, *() and [], *{} or [] and (), *{} and [] or (), 2)
# Here we add keyword arguments
def call_f_kwargs():
"""
>>> call_f_kwargs()
(1, 2, 3) {'a': 4, 'b': 5}
(1, 2, 3, 4, 5) {'a': 6, 'b': 7}
(1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
(1, 2, 3, 4, 5) {'a': 6, 'b': 7, 'c': 8}
(1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7}
(1, 2, 3) {'a': 4, 'b': 5}
(1, 2, 3, 4, 5) {'a': 6, 'b': 7}
(1, 2, 3, 6, 7) {'a': 8, 'b': 9, 'x': 4, 'y': 5}
(1, 2, 3, 4, 5) {'a': 8, 'b': 9, 'x': 6, 'y': 7}
(1, 2) {'a': 3}
"""
f(1, 2, 3, **{'a':4, 'b':5})
f(1, 2, 3, *[4, 5], **{'a':6, 'b':7})
f(1, 2, 3, x=4, y=5, *(6, 7), **{'a':8, 'b': 9})
f(1, 2, 3, *[4, 5], **{'c': 8}, **{'a':6, 'b':7})
f(1, 2, 3, *(4, 5), x=6, y=7, **{'a':8, 'b': 9})
f(1, 2, 3, **UserDict(a=4, b=5))
f(1, 2, 3, *(4, 5), **UserDict(a=6, b=7))
f(1, 2, 3, x=4, y=5, *(6, 7), **UserDict(a=8, b=9))
f(1, 2, 3, *(4, 5), x=6, y=7, **UserDict(a=8, b=9))
f(1, *[] or () and {}, *() and [], *{} or [] and (), *{} and [] or (), 2,
**{} and {} or {}, **{} or {} and {}, **{} and {}, a=3)
# Examples with invalid arguments (TypeErrors). We're also testing the function
# names in the exception messages.
#
# Verify clearing of SF bug #733667
def errors_f1():
"""
>>> errors_f1() # doctest: +ELLIPSIS
Traceback (most recent call last):
...
TypeError: ...got multiple values for keyword argument 'a'
"""
f(1, 2, **{'a': -1, 'b': 5}, **{'a': 4, 'c': 6})
def errors_f2():
"""
>>> errors_f2() # doctest: +ELLIPSIS
Traceback (most recent call last):
...
TypeError: ...multiple values for keyword argument 'a'
"""
f(1, 2, **{'a': -1, 'b': 5}, a=4, c=6)
def errors_e1():
"""
>>> try: errors_e1()
... except TypeError: pass
... else: print("FAILED!")
"""
e(c=4)
def errors_e2():
"""
>>> try: errors_e2()
... except TypeError: pass
... else: print("FAILED!")
"""
e(a=1, b=2, c=4)
def errors_g1():
"""
>>> errors_g1()
Traceback (most recent call last):
...
TypeError: g() takes at least 1 positional argument (0 given)
# TypeError: g() missing 1 required positional argument: 'x'
"""
g()
def errors_g2():
"""
>>> errors_g2()
Traceback (most recent call last):
...
TypeError: g() takes at least 1 positional argument (0 given)
# TypeError: g() missing 1 required positional argument: 'x'
"""
g(*())
def errors_g3():
"""
>>> errors_g3()
Traceback (most recent call last):
...
TypeError: g() takes at least 1 positional argument (0 given)
# TypeError: g() missing 1 required positional argument: 'x'
"""
g(*(), **{})
def call_g_positional():
"""
>>> call_g_positional()
1 () {}
1 (2,) {}
1 (2, 3) {}
1 (2, 3, 4, 5) {}
"""
g(1)
g(1, 2)
g(1, 2, 3)
g(1, 2, 3, *(4, 5))
def call_nonseq_positional1():
"""
>>> call_nonseq_positional1()
Traceback (most recent call last):
...
TypeError: 'Nothing' object is not iterable
# TypeError: g() argument after * must be a sequence, not Nothing
"""
class Nothing(object): pass
g(*Nothing())
def call_nonseq_positional2():
"""
>>> call_nonseq_positional2()
Traceback (most recent call last):
...
TypeError: 'Nothing' object is not iterable
# TypeError: g() argument after * must be a sequence, not Nothing
"""
class Nothing(object):
def __len__(self): return 5
g(*Nothing())
def call_seqlike_positional1():
"""
>>> call_seqlike_positional1()
0 (1, 2) {}
"""
class Nothing(object):
def __len__(self): return 5
def __getitem__(self, i):
if i<3: return i
else: raise IndexError(i)
g(*Nothing())
def call_seqlike_positional2():
"""
>>> call_seqlike_positional2()
0 (1, 2, 3) {}
"""
class Nothing:
def __init__(self): self.c = 0
def __iter__(self): return self
def __next__(self):
if self.c == 4:
raise StopIteration
c = self.c
self.c += 1
return c
next = __next__
g(*Nothing())
# Make sure that the function doesn't stomp the dictionary
def call_kwargs_unmodified1():
"""
>>> call_kwargs_unmodified1()
1 () {'a': 1, 'b': 2, 'c': 3, 'd': 4}
True
"""
d = {'a': 1, 'b': 2, 'c': 3}
d2 = d.copy()
g(1, d=4, **d)
return d == d2
# What about willful misconduct?
def call_kwargs_unmodified2():
"""
>>> call_kwargs_unmodified2()
{}
"""
def saboteur(**kw):
kw['x'] = 'm'
return kw
d = {}
kw = saboteur(a=1, **d)
return d
def errors_args_kwargs_overlap():
"""
>>> errors_args_kwargs_overlap() # doctest: +ELLIPSIS
Traceback (most recent call last):
...
TypeError: ...got multiple values for... argument 'x'
"""
g(1, 2, 3, **{'x': 4, 'y': 5})
def errors_non_string_kwarg():
"""
>>> errors_non_string_kwarg() # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: ...keywords must be strings
"""
f(**{1:2})
def errors_unexpected_kwarg():
"""
>>> errors_unexpected_kwarg()
Traceback (most recent call last):
...
TypeError: h() got an unexpected keyword argument 'e'
"""
h(**{'e': 2})
def errors_call_nonseq():
"""
>>> try: errors_call_nonseq()
... except TypeError: pass
... else: print("FAILED!")
"""
h(*h)
def errors_call_builtin_nonseq():
"""
>>> try: errors_call_builtin_nonseq()
... except TypeError: pass
... else: print("FAILED!")
"""
dir(*h)
def errors_call_none_nonseq():
"""
>>> try: errors_call_none_nonseq()
... except TypeError: pass
... else: print("FAILED!")
"""
None(*h)
def errors_call_nonmapping_kwargs():
"""
>>> try: errors_call_nonmapping_kwargs()
... except TypeError: pass
... else: print("FAILED!")
"""
h(**h)
def errors_call_builtin_nonmapping_kwargs():
"""
>>> try: errors_call_builtin_nonmapping_kwargs()
... except TypeError: pass
... else: print("FAILED!")
"""
dir(**h)
def errors_call_none_nonmapping_kwargs():
"""
>>> try: errors_call_none_nonmapping_kwargs()
... except TypeError: pass
... else: print("FAILED!")
"""
None(**h)
''' # compile time error in Cython
def errors_call_builtin_duplicate_kwarg():
"""
>>> errors_call_builtin_duplicate_kwarg() # doctest: +ELLIPSIS
Traceback (most recent call last):
...
TypeError: ...got multiple values for keyword argument 'b'
"""
dir(b=1, **{'b': 1})
'''
# Another helper function
def f2(*a, **b):
return a, b
def call_many_kwargs():
"""
call_many_kwargs()
(3, 512, True)
"""
d = {}
for i in range(512):
key = 'k%d' % i
d[key] = i
a, b = f2(1, *(2,3), **d)
return len(a), len(b), b == d
def call_method(Foo):
"""
>>> class Foo(object):
... def method(self, arg1, arg2):
... print(arg1+arg2)
>>> call_method(Foo)
3
3
5
5
"""
x = Foo()
Foo.method(*(x, 1, 2))
Foo.method(x, *(1, 2))
if sys.version_info[0] >= 3:
Foo.method(*(1, 2, 3))
Foo.method(1, *[2, 3])
else:
print(5)
print(5)
# A PyCFunction that takes only positional parameters should allow an
# empty keyword dictionary to pass without a complaint, but raise a
# TypeError if te dictionary is not empty
def call_builtin_empty_dict():
"""
>>> call_builtin_empty_dict()
"""
silence = id(1, *{})
silence = id(1, **{})
def call_builtin_nonempty_dict():
"""
>>> call_builtin_nonempty_dict()
Traceback (most recent call last):
...
TypeError: id() takes no keyword arguments
"""
return id(1, **{'foo': 1})
''' Cython: currently just passes empty kwargs into f() while CPython keeps the content
# A corner case of keyword dictionary items being deleted during
# the function call setup. See <http://bugs.python.org/issue2016>.
def call_kwargs_modified_while_building():
"""
>>> call_kwargs_modified_while_building()
1 2
"""
class Name(str):
def __eq__(self, other):
try:
del x[self]
except KeyError:
pass
return str.__eq__(self, other)
def __hash__(self):
return str.__hash__(self)
x = {Name("a"):1, Name("b"):2}
def f(a, b):
print(a,b)
f(**x)
'''
# Too many arguments:
def errors_call_one_arg(f):
"""
>>> def f(): pass
>>> try: errors_call_one_arg(f)
... except TypeError: pass
... else: print("FAILED!")
"""
f(1)
def errors_call_2args(f):
"""
>>> def f(a): pass
>>> try: errors_call_2args(f)
... except TypeError: pass
... else: print("FAILED!")
"""
f(1, 2)
def errors_call_3args(f):
"""
>>> def f(a, b=1): pass
>>> try: errors_call_3args(f)
... except TypeError: pass
... else: print("FAILED!")
"""
f(1, 2, 3)
def errors_call_1arg_1kwarg(f):
# Py3 only
f(1, kw=3)
def errors_call_3args_2kwargs(f):
# Py3 only
f(1, 2, 3, b=3, kw=3)
def errors_call_3args_1kwarg(f):
# Py3 only
f(2, 3, 4, kw=4)
# Too few and missing arguments:
def errors_call_no_args(f):
"""
>>> def f(a): pass
>>> try: errors_call_no_args(f)
... except TypeError: pass
... else: print("FAILED!")
>>> def f(a, b): pass
>>> try: errors_call_no_args(f)
... except TypeError: pass
... else: print("FAILED!")
>>> def f(a, b, c): pass
>>> try: errors_call_no_args(f)
... except TypeError: pass
... else: print("FAILED!")
>>> def f(a, b, c, d, e): pass
>>> try: errors_call_no_args(f)
... except TypeError: pass
... else: print("FAILED!")
"""
f()
def errors_call_one_missing_kwarg(f):
"""
>>> def f(a, b=4, c=5, d=5): pass
>>> try: errors_call_one_missing_kwarg(f)
... except TypeError: pass
... else: print("FAILED!")
"""
f(c=12, b=9)