1"""
2Test the 'with gil:' statement.
3"""
4
5cimport cython
6from cpython.ref cimport PyObject
7
8import sys
9
10
11def redirect_stderr(func, *args, **kwargs):
12    """
13    Helper function that redirects stderr to stdout for doctest.
14    """
15    stderr, sys.stderr = sys.stderr, sys.stdout
16    func(*args, **kwargs)
17    sys.stderr = stderr
18
19cdef void puts(char *string) with gil:
20    """
21    We need this for doctest, used from nogil sections.
22    """
23    print string.decode('ascii')
24
25class ExceptionWithMsg(Exception):
26    """
27    In python2.4 Exception is formatted as <exceptions.Exception
28    instance at 0x1b8f948> when swallowed.
29    """
30
31    def __repr__(self):
32        return "ExceptionWithMsg(%r)" % self.args
33
34
35# Start with some normal Python functions
36
37def test_simple():
38    """
39    >>> test_simple()
40    ['spam', 'ham']
41    """
42    with nogil:
43        with gil:
44            print ['spam', 'ham']
45
46def test_nested_gil_blocks():
47    """
48    >>> test_nested_gil_blocks()
49    entered outer nogil section
50    entered outer gil section
51    entered inner nogil section
52    entered inner gil section
53    leaving inner gil section
54    leaving inner nogil section
55    leaving outer gil section
56    leaving outer nogil section
57    """
58
59    with nogil:
60        puts("entered outer nogil section")
61
62        with gil:
63            print 'entered outer gil section'
64
65            with nogil:
66                puts("entered inner nogil section")
67                with gil:
68                    print 'entered inner gil section'
69                    print 'leaving inner gil section'
70                puts("leaving inner nogil section")
71
72            print "leaving outer gil section"
73        puts("leaving outer nogil section")
74
75def test_propagate_exception():
76    """
77    >>> test_propagate_exception()
78    Traceback (most recent call last):
79        ...
80    Exception: This exception propagates!
81    """
82    # Note, doctest doesn't support both output and exceptions
83    with nogil:
84        with gil:
85            raise Exception("This exception propagates!")
86
87def test_catch_exception():
88    """
89    >>> test_catch_exception()
90    This is executed
91    Exception value
92    This is also executed
93    """
94    try:
95        with nogil:
96            with gil:
97                print "This is executed"
98                raise Exception("Exception value")
99                print "This is not executed"
100            puts("This is also not executed")
101    except Exception, e:
102        print e
103    print "This is also executed"
104
105def test_try_finally_and_outer_except():
106    """
107    >>> test_try_finally_and_outer_except()
108    First finally clause
109    Second finally clause
110    Caught: Some Exception
111    End of function
112    """
113    try:
114
115        with nogil:
116            with gil:
117                try:
118                    with nogil:
119                        with gil:
120                            try:
121                                raise Exception("Some Exception")
122                            finally:
123                                puts("First finally clause")
124                finally:
125                    puts("Second finally clause")
126            puts("This is not executed")
127
128    except Exception, e:
129        print "Caught:", e
130
131    print "End of function"
132
133def test_restore_exception():
134    """
135    >>> test_restore_exception()
136    Traceback (most recent call last):
137        ...
138    Exception: Override the raised exception
139    """
140    with nogil:
141        with gil:
142            try:
143                with nogil:
144                    with gil:
145                        raise Exception("Override this please")
146            finally:
147                raise Exception("Override the raised exception")
148
149### DISABLED: this cannot work with flow control analysis
150##
151## def test_declared_variables():
152##     """
153##     >>> test_declared_variables()
154##     None
155##     None
156##     ['s', 'p', 'a', 'm']
157##     ['s', 'p', 'a', 'm']
158##     """
159##     cdef object somevar
160##
161##     print somevar
162##
163##     with nogil:
164##         with gil:
165##             print somevar
166##             somevar = list("spam")
167##             print somevar
168##
169##     print somevar
170
171### DISABLED: this cannot work with flow control analysis
172##
173## def test_undeclared_variables():
174##     """
175##     >>> test_undeclared_variables()
176##     None
177##     None
178##     ['s', 'p', 'a', 'm']
179##     ['s', 'p', 'a', 'm']
180##     """
181##     print somevar
182##     with nogil:
183##         with gil:
184##             print somevar
185##             somevar = list("spam")
186##             print somevar
187##
188##     print somevar
189
190def test_loops_and_boxing():
191    """
192    >>> test_loops_and_boxing()
193    spamham
194    h
195    a
196    m
197    done looping
198    """
199    cdef char c, *string = "spamham"
200
201    with nogil:
202        with gil:
203            print string.decode('ascii')
204            for c in string[4:]:
205                print "%c" % c
206            else:
207                print "done looping"
208
209cdef class SomeExtClass(object):
210    cdef int some_attribute
211
212@cython.infer_types(True)
213def test_infer_types():
214    """
215    >>> test_infer_types()
216    10
217    """
218    with nogil:
219        with gil:
220            obj = SomeExtClass()
221            obj.some_attribute = 10
222
223    print obj.some_attribute
224
225def test_closure():
226    """
227    >>> test_closure()
228    Traceback (most recent call last):
229        ...
230    Exception: {'twinkle': 'little star'}
231    """
232    a = dict(twinkle='little star')
233
234    def inner_function():
235        with nogil:
236            with gil:
237                raise Exception(a)
238
239    with nogil:
240        with gil:
241            inner_function()
242
243    raise Exception("This should not be raised!")
244
245cpdef test_cpdef():
246    """
247    >>> test_cpdef()
248    Seems to work!
249    Or does it?
250    """
251    with nogil:
252        with gil:
253            print "Seems to work!"
254        puts("Or does it?")
255
256
257# Now test some cdef functions with different return types
258
259cdef void void_nogil_ignore_exception() nogil:
260    with gil:
261        raise ExceptionWithMsg("This is swallowed")
262
263    puts("unreachable")
264    with gil:
265        print "unreachable"
266
267cdef void void_nogil_nested_gil() nogil:
268    with gil:
269        with nogil:
270            with gil:
271                print 'Inner gil section'
272            puts("nogil section")
273        raise ExceptionWithMsg("Swallow this")
274    puts("Don't print this")
275
276def test_nogil_void_funcs_with_gil():
277    """
278    >>> redirect_stderr(test_nogil_void_funcs_with_gil)  # doctest: +ELLIPSIS
279    with_gil.ExceptionWithMsg: This is swallowed
280    Exception... ignored...
281    Inner gil section
282    nogil section
283    ...
284    Exception... ignored...
285    """
286    void_nogil_ignore_exception()
287    void_nogil_nested_gil()
288
289def test_nogil_void_funcs_with_nogil():
290    """
291    >>> redirect_stderr(test_nogil_void_funcs_with_nogil)  # doctest: +ELLIPSIS
292    with_gil.ExceptionWithMsg: This is swallowed
293    Exception... ignored...
294    Inner gil section
295    nogil section
296    with_gil.ExceptionWithMsg: Swallow this
297    Exception... ignored...
298    """
299    with nogil:
300        void_nogil_ignore_exception()
301        void_nogil_nested_gil()
302
303
304cdef PyObject *nogil_propagate_exception() nogil except NULL:
305    with nogil:
306        with gil:
307            raise Exception("This exception propagates!")
308    return <PyObject *> 1
309
310def test_nogil_propagate_exception():
311    """
312    >>> test_nogil_propagate_exception()
313    Traceback (most recent call last):
314        ...
315    Exception: This exception propagates!
316    """
317    nogil_propagate_exception()
318
319
320cdef with_gil_raise() with gil:
321    raise Exception("This exception propagates!")
322
323def test_release_gil_call_gil_func():
324    """
325    >>> test_release_gil_call_gil_func()
326    Traceback (most recent call last):
327        ...
328    Exception: This exception propagates!
329    """
330    with nogil:
331        with gil:
332            with_gil_raise()
333
334
335# Test try/finally in nogil blocks
336
337def test_try_finally_in_nogil():
338    """
339    >>> test_try_finally_in_nogil()
340    Traceback (most recent call last):
341        ...
342    Exception: Override exception!
343    """
344    with nogil:
345        try:
346            with gil:
347                raise Exception("This will be overridden")
348        finally:
349            with gil:
350                raise Exception("Override exception!")
351
352            with gil:
353                raise Exception("This code should not be executed!")
354
355def test_nogil_try_finally_no_exception():
356    """
357    >>> test_nogil_try_finally_no_exception()
358    first nogil try
359    nogil try gil
360    second nogil try
361    nogil finally
362    ------
363    First with gil block
364    Second with gil block
365    finally block
366    """
367    with nogil:
368        try:
369            puts("first nogil try")
370            with gil:
371                print "nogil try gil"
372            puts("second nogil try")
373        finally:
374            puts("nogil finally")
375
376    print '------'
377
378    with nogil:
379        try:
380            with gil:
381                print "First with gil block"
382
383            with gil:
384                print "Second with gil block"
385        finally:
386            puts("finally block")
387
388def test_nogil_try_finally_propagate_exception():
389    """
390    >>> test_nogil_try_finally_propagate_exception()
391    Execute finally clause
392    Propagate this!
393    """
394    try:
395        with nogil:
396            try:
397                with gil:
398                    raise Exception("Propagate this!")
399                with gil:
400                    raise Exception("Don't reach this section!")
401            finally:
402                puts("Execute finally clause")
403    except Exception, e:
404        print e
405
406def test_nogil_try_finally_return_in_with_gil(x):
407    """
408    >>> test_nogil_try_finally_return_in_with_gil(10)
409    print me
410    10
411    """
412    with nogil:
413        try:
414            with gil:
415                raise Exception("Swallow me!")
416        finally:
417            with gil:
418                print "print me"
419                return x
420
421    print "I am not executed"
422
423cdef void nogil_try_finally_return() nogil:
424    try:
425        with gil:
426            raise Exception("I am swallowed in nogil code... right?")
427    finally:
428        with gil:
429            print "print me first"
430
431        return
432
433    with gil:
434        print "I am not executed"
435
436def test_nogil_try_finally_return():
437    """
438    >>> test_nogil_try_finally_return()
439    print me first
440    """
441    with nogil:
442        nogil_try_finally_return()
443
444cdef int error_func() except -1 with gil:
445    raise Exception("propagate this")
446
447def test_nogil_try_finally_error_label():
448    """
449    >>> test_nogil_try_finally_error_label()
450    print me first
451    propagate this
452    """
453    try:
454        with nogil:
455            try:
456                error_func()
457            finally:
458                with gil: print "print me first"
459    except Exception, e:
460        print e.args[0]
461
462
463cdef void test_timing_callback() with gil:
464  pass
465
466def test_timing(long N):
467  """
468  >>> sorted([test_timing(10000) for _ in range(10)])  # doctest: +ELLIPSIS
469  [...]
470  """
471  import time
472  t = time.time()
473  with nogil:
474    for _ in range(N):
475      test_timing_callback()
476  return time.time() - t
477