1# cython: binding=True
2# mode: run
3# tag: cyfunction, closures
4
5cimport cython
6import sys
7
8def get_defaults(func):
9    if sys.version_info >= (2, 6, 0):
10        return func.__defaults__
11    return func.func_defaults
12
13def test_defaults_none():
14    """
15    >>> get_defaults(test_defaults_none)
16    """
17
18def test_defaults_literal(a=1, b=(1,2,3)):
19    """
20    >>> get_defaults(test_defaults_literal) is get_defaults(test_defaults_literal)
21    True
22    >>> get_defaults(test_defaults_literal)
23    (1, (1, 2, 3))
24    >>> a, b = get_defaults(test_defaults_literal)
25    >>> c, d = test_defaults_literal()
26    >>> a is c
27    True
28    >>> b is d
29    True
30    """
31    return a, b
32
33def test_defaults_nonliteral():
34    """
35    >>> f0, f1 = test_defaults_nonliteral()
36    >>> get_defaults(f0) is get_defaults(f0) # cached
37    True
38    >>> get_defaults(f0)
39    (0, {}, (1, 2, 3))
40    >>> a, b = get_defaults(f0)[1:]
41    >>> c, d = f0(0)
42    >>> a is c
43    True
44    >>> b is d
45    True
46    >>> get_defaults(f1) is get_defaults(f1) # cached
47    True
48    >>> get_defaults(f1)
49    (0, [], (1, 2, 3))
50    >>> a, b = get_defaults(f1)[1:]
51    >>> c, d = f1(0)
52    >>> a is c
53    True
54    >>> b is d
55    True
56    """
57    ret = []
58    for i in {}, []:
59        def foo(a, b=0, c=i, d=(1,2,3)):
60            return c, d
61        ret.append(foo)
62    return ret
63
64_counter = 0
65def counter():
66    global _counter
67    _counter += 1
68    return _counter
69
70def test_defaults_nonliteral_func_call(f):
71    """
72    >>> f = test_defaults_nonliteral_func_call(counter)
73    >>> f()
74    1
75    >>> get_defaults(f)
76    (1,)
77    >>> f = test_defaults_nonliteral_func_call(lambda: list())
78    >>> f()
79    []
80    >>> get_defaults(f)
81    ([],)
82    >>> get_defaults(f)[0] is f()
83    True
84    """
85    def func(a=f()):
86        return a
87    return func
88
89
90def cy_kwonly_default_args(a, x=1, *, b=2):
91    l = m = 1
92
93def test_kwdefaults(value):
94    """
95    >>> cy_kwonly_default_args.__defaults__
96    (1,)
97    >>> cy_kwonly_default_args.func_defaults
98    (1,)
99
100    >>> cy_kwonly_default_args.__kwdefaults__
101    {'b': 2}
102
103    >>> test_kwdefaults.__defaults__
104    >>> test_kwdefaults.__kwdefaults__
105
106    >>> f = test_kwdefaults(5)
107    >>> f.__defaults__
108    (1,)
109    >>> f.__kwdefaults__
110    {'b': 5}
111    >>> f.__kwdefaults__ = ()
112    Traceback (most recent call last):
113    TypeError: __kwdefaults__ must be set to a dict object
114    >>> f.__kwdefaults__ = None
115    >>> f.__kwdefaults__
116    >>> f.__kwdefaults__ = {}
117    >>> f.__kwdefaults__
118    {}
119    >>> f.__kwdefaults__ = {'a': 2}
120    >>> f.__kwdefaults__
121    {'a': 2}
122    """
123    def kwonly_default_args(a, x=1, *, b=value):
124        return a, x, b
125    return kwonly_default_args
126
127
128_counter2 = 1.0
129def counter2():
130    global _counter2
131    _counter2 += 1.0
132    return _counter2
133
134def test_defaults_fused(cython.floating arg1, cython.floating arg2 = counter2()):
135    """
136    >>> test_defaults_fused(1.0)
137    1.0 2.0
138    >>> test_defaults_fused(1.0, 3.0)
139    1.0 3.0
140    >>> _counter2
141    2.0
142
143    >>> get_defaults(test_defaults_fused)
144    (2.0,)
145    >>> get_defaults(test_defaults_fused[float])
146    (2.0,)
147    """
148    print arg1, arg2
149
150funcs = []
151for i in range(10):
152    def defaults_fused(cython.floating a, cython.floating b = i):
153        return a, b
154    funcs.append(defaults_fused)
155
156def test_dynamic_defaults_fused():
157    """
158    >>> test_dynamic_defaults_fused()
159    i 0 func result (1.0, 0.0) defaults (0,)
160    i 1 func result (1.0, 1.0) defaults (1,)
161    i 2 func result (1.0, 2.0) defaults (2,)
162    i 3 func result (1.0, 3.0) defaults (3,)
163    i 4 func result (1.0, 4.0) defaults (4,)
164    i 5 func result (1.0, 5.0) defaults (5,)
165    i 6 func result (1.0, 6.0) defaults (6,)
166    i 7 func result (1.0, 7.0) defaults (7,)
167    i 8 func result (1.0, 8.0) defaults (8,)
168    i 9 func result (1.0, 9.0) defaults (9,)
169    """
170    for i, f in enumerate(funcs):
171        print "i", i, "func result", f(1.0), "defaults", get_defaults(f)
172
173
174def test_memoryview_none(const unsigned char[:] b=None):
175    """
176    >>> test_memoryview_none()
177    >>> test_memoryview_none(None)
178    >>> test_memoryview_none(b'abc')
179    97
180    """
181    if b is None:
182        return None
183    return b[0]
184
185
186def test_memoryview_bytes(const unsigned char[:] b=b'xyz'):
187    """
188    >>> test_memoryview_bytes()
189    120
190    >>> test_memoryview_bytes(None)
191    >>> test_memoryview_bytes(b'abc')
192    97
193    """
194    if b is None:
195        return None
196    return b[0]
197
198
199@cython.test_fail_if_path_exists(
200    '//NameNode[@entry.in_closure = True]',
201    '//NameNode[@entry.from_closure = True]')
202def test_func_default_inlined():
203    """
204    Make sure we don't accidentally generate a closure.
205
206    >>> func = test_func_default_inlined()
207    >>> func()
208    1
209    >>> func(2)
210    2
211    """
212    def default():
213        return 1
214    def func(arg=default()):
215        return arg
216    return func
217
218
219@cython.test_fail_if_path_exists(
220    '//NameNode[@entry.in_closure = True]',
221    '//NameNode[@entry.from_closure = True]')
222def test_func_default_scope():
223    """
224    Test that the default value expression is evaluated in the outer scope.
225
226    >>> func = test_func_default_scope()
227    3
228    >>> func()
229    [0, 1, 2, 3]
230    >>> func(2)
231    2
232    """
233    i = -1
234    def func(arg=[ i for i in range(4) ]):
235        return arg
236    print i  # list comps leak in Py2 mode => i == 3
237    return func
238
239
240def test_func_default_scope_local():
241    """
242    >>> func = test_func_default_scope_local()
243    -1
244    >>> func()
245    [0, 1, 2, 3]
246    >>> func(2)
247    2
248    """
249    i = -1
250    def func(arg=list(i for i in range(4))):
251        return arg
252    print i  # genexprs don't leak
253    return func
254
255cdef class C:
256    def f1(self, a, b=1, c=[]):
257        pass
258    def f2(self, a, b=1,/, c=[1]):
259        pass
260    def f3(self, a, /, b=1, *, c=[1]):
261        pass
262    cpdef f4(self, a, char*c=NULL):
263        pass
264    cpdef f5(self, a, str s = "123"):
265        pass
266    cpdef f6(self, a, int s = 4):
267        pass
268    cpdef f7(self, a, dict s = {'a':22}):
269        pass
270    cpdef f8(self, a, list s = [15]):
271        pass
272
273
274def check_defaults_on_methods_for_introspection():
275    """
276    >>> C.f1.__defaults__
277    (1, [])
278    >>> C.f1.__kwdefaults__
279    >>> C.f2.__defaults__
280    (1, [1])
281    >>> C.f2.__kwdefaults__
282    >>> C.f3.__defaults__
283    (1,)
284    >>> C.f3.__kwdefaults__
285    {'c': [1]}
286    >>> C.f4.__defaults__
287    >>> C.f4.__kwdefaults__
288    >>> C.f5.__defaults__
289    ('123',)
290    >>> C.f5.__kwdefaults__
291    >>> C.f6.__defaults__
292    (4,)
293    >>> C.f6.__kwdefaults__
294    >>> C.f7.__defaults__
295    ({'a': 22},)
296    >>> C.f7.__kwdefaults__
297    >>> C.f8.__defaults__
298    ([15],)
299    >>> C.f8.__kwdefaults__
300    """
301    pass
302