1#     Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com
2#
3#     Python tests originally created or extracted from other peoples work. The
4#     parts were too small to be protected.
5#
6#     Licensed under the Apache License, Version 2.0 (the "License");
7#     you may not use this file except in compliance with the License.
8#     You may obtain a copy of the License at
9#
10#        http://www.apache.org/licenses/LICENSE-2.0
11#
12#     Unless required by applicable law or agreed to in writing, software
13#     distributed under the License is distributed on an "AS IS" BASIS,
14#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15#     See the License for the specific language governing permissions and
16#     limitations under the License.
17#
18""" Reference counting tests for Python3.6 or higher.
19
20These contain functions that do specific things, where we have a suspect
21that references may be lost or corrupted. Executing them repeatedly and
22checking the reference count is how they are used.
23
24These are Python3.6 specific constructs, that will give a SyntaxError or
25not be relevant on older versions.
26"""
27
28import os
29import sys
30
31# While we use that for comparison code, no need to compile that.
32# nuitka-project: --nofollow-import-to=nuitka
33
34# Find nuitka package relative to us.
35sys.path.insert(
36    0,
37    os.path.normpath(
38        os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..")
39    ),
40)
41
42# isort:start
43
44import asyncio
45import types
46
47from nuitka.tools.testing.Common import (
48    async_iterate,
49    executeReferenceChecked,
50    run_async,
51)
52
53
54class AwaitException(Exception):
55    pass
56
57
58def run_until_complete(coro):
59    exc = False
60    while True:
61        try:
62            if exc:
63                exc = False
64                fut = coro.throw(AwaitException)
65            else:
66                fut = coro.send(None)
67        except StopIteration as ex:
68            return ex.args[0]
69
70        if fut == ("throw",):
71            exc = True
72
73
74def simpleFunction1():
75    async def gen1():
76        try:
77            yield
78        except:  # pylint: disable=bare-except
79            pass
80
81    async def run():
82        g = gen1()
83        await g.asend(None)
84        await g.asend(None)
85
86    try:
87        run_async(run())
88    except StopAsyncIteration:
89        pass
90
91
92def simpleFunction2():
93    async def async_gen():
94        try:
95            yield 1
96            yield 1.1
97            1 / 0  # pylint: disable=pointless-statement
98        finally:
99            yield 2
100            yield 3
101
102        yield 100
103
104    async_iterate(async_gen())
105
106
107@types.coroutine
108def awaitable(*, throw=False):
109    if throw:
110        yield ("throw",)
111    else:
112        yield ("result",)
113
114
115async def gen2():
116    await awaitable()
117    a = yield 123
118    assert a is None
119    await awaitable()
120    yield 456
121    await awaitable()
122    yield 789
123
124
125def simpleFunction3():
126    def to_list(gen):
127        async def iterate():
128            res = []
129            async for i in gen:
130                res.append(i)
131            return res
132
133        return run_until_complete(iterate())
134
135    async def run2():
136        return to_list(gen2())
137
138    run_async(run2())
139
140
141def simpleFunction4():
142    g = gen2()
143    ai = g.__aiter__()
144    an = ai.__anext__()
145    an.__next__()
146
147    try:
148        ai.__anext__().__next__()
149    except StopIteration as _ex:
150        pass
151    except RuntimeError:
152        # Python 3.8 doesn't like this anymore
153        assert sys.version_info >= (3, 8)
154
155    try:
156        ai.__anext__().__next__()
157    except RuntimeError:
158        # Python 3.8 doesn't like this anymore
159        assert sys.version_info >= (3, 8)
160
161
162def simpleFunction5():
163    t = 2
164
165    class C:  # pylint: disable=invalid-name
166        exec("u=2")  # pylint: disable=exec-used
167        x: int = 2
168        y: float = 2.0
169
170        z = x + y + t * u  # pylint: disable=undefined-variable
171
172        rawdata = b"The quick brown fox jumps over the lazy dog.\r\n"
173        # Be slow so we don't depend on other modules
174        rawdata += bytes(range(256))
175
176    return C()
177
178
179# This refleaks big time, but the construct is rare enough to not bother
180# as this proves hard to find.
181def disabled_simpleFunction6():
182    loop = asyncio.new_event_loop()
183    asyncio.set_event_loop(None)
184
185    async def waiter(timeout):
186        await asyncio.sleep(timeout)
187        yield 1
188
189    async def wait():
190        async for _ in waiter(1):
191            pass
192
193    t1 = loop.create_task(wait())
194    t2 = loop.create_task(wait())
195
196    loop.run_until_complete(asyncio.sleep(0.01))
197
198    t1.cancel()
199    t2.cancel()
200
201    try:
202        loop.run_until_complete(t1)
203    except asyncio.CancelledError:
204        pass
205    try:
206        loop.run_until_complete(t2)
207    except asyncio.CancelledError:
208        pass
209
210    loop.run_until_complete(loop.shutdown_asyncgens())
211
212    loop.close()
213
214
215# These need stderr to be wrapped.
216tests_stderr = ()
217
218# Disabled tests
219tests_skipped = {}
220
221result = executeReferenceChecked(
222    prefix="simpleFunction",
223    names=globals(),
224    tests_skipped=tests_skipped,
225    tests_stderr=tests_stderr,
226)
227
228sys.exit(0 if result else 1)
229