File: test_evaluate_expression.py

package info (click to toggle)
pydevd 3.3.0%2Bds-4
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 13,892 kB
  • sloc: python: 77,508; cpp: 1,869; sh: 368; makefile: 50; ansic: 4
file content (344 lines) | stat: -rw-r--r-- 11,019 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
from _pydevd_bundle.pydevd_constants import IS_PY313_OR_GREATER, IS_PY38_OR_GREATER, NULL, IS_PY313_0, IS_PY313_1
from _pydevd_bundle.pydevd_xml import ExceptionOnEvaluate

import sys
import pytest

SOME_LST = ["foo", "bar"]
BAR = "bar"
FOO = "foo"
global_frame = sys._getframe()


def obtain_frame():
    A = 1
    B = 2
    yield sys._getframe()


@pytest.fixture
def disable_critical_log():
    # We want to hide the logging related to _evaluate_with_timeouts not receiving the py_db.
    from _pydev_bundle.pydev_log import log_context
    import io

    stream = io.StringIO()
    with log_context(0, stream):
        yield


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_basic(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        evaluate_expression(None, frame, "some_var = 1", is_exec=True)

        assert frame.f_locals["some_var"] == 1

    check(next(iter(obtain_frame())))
    assert "some_var" not in sys._getframe().f_globals

    # as locals == globals, this will also change the current globals
    check(global_frame)
    assert "some_var" in sys._getframe().f_globals
    del sys._getframe().f_globals["some_var"]
    assert "some_var" not in sys._getframe().f_globals


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_1(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = """
container = ["abc","efg"]
results = []
for s in container:
    result = [s[i] for i in range(3)]
    results.append(result)
"""
        evaluate_expression(None, frame, eval_txt, is_exec=True)
        assert frame.f_locals["results"] == [["a", "b", "c"], ["e", "f", "g"]]
        assert frame.f_locals["s"] == "efg"

    check(next(iter(obtain_frame())))

    for varname in ["container", "results", "s"]:
        assert varname not in sys._getframe().f_globals

    check(global_frame)
    for varname in ["container", "results", "s"]:
        assert varname in sys._getframe().f_globals

    for varname in ["container", "results", "s"]:
        del sys._getframe().f_globals[varname]


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_2(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = "all((x in (BAR, FOO) for x in SOME_LST))"
        assert evaluate_expression(None, frame, eval_txt, is_exec=False)

    check(next(iter(obtain_frame())))
    check(global_frame)


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_3(disable_critical_log):
    if not IS_PY38_OR_GREATER:
        return

    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = """11 if (some_var := 22) else 33"""
        assert evaluate_expression(None, frame, eval_txt, is_exec=False) == 11

    check(next(iter(obtain_frame())))
    assert "some_var" not in sys._getframe().f_globals

    # as locals == globals, this will also change the current globals
    check(global_frame)
    assert "some_var" in sys._getframe().f_globals
    del sys._getframe().f_globals["some_var"]
    assert "some_var" not in sys._getframe().f_globals


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_4(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = """import email;email.foo_value"""
        with pytest.raises(AttributeError):
            evaluate_expression(None, frame, eval_txt, is_exec=True)
        assert "email" in frame.f_locals

    check(next(iter(obtain_frame())))
    assert "email" not in sys._getframe().f_globals

    # as locals == globals, this will also change the current globals
    check(global_frame)
    assert "email" in sys._getframe().f_globals
    del sys._getframe().f_globals["email"]
    assert "email" not in sys._getframe().f_globals


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_access_globals(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = """globals()['global_variable'] = 22"""
        evaluate_expression(None, frame, eval_txt, is_exec=True)
        assert "global_variable" not in frame.f_locals
        assert "global_variable" in frame.f_globals

    check(next(iter(obtain_frame())))
    assert "global_variable" in sys._getframe().f_globals
    assert "global_variable" not in sys._getframe().f_locals


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_create_none(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = "x = None"
        evaluate_expression(None, frame, eval_txt, is_exec=True)
        assert "x" in frame.f_locals
        assert "x" not in frame.f_globals

    check(next(iter(obtain_frame())))


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_delete_var(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = "x = 22"
        evaluate_expression(None, frame, eval_txt, is_exec=True)
        assert "x" in frame.f_locals

        eval_txt = "del x"
        evaluate_expression(None, frame, eval_txt, is_exec=True)
        if IS_PY313_0:
            assert frame.f_locals["x"] == None
        else:
            assert "x" not in frame.f_locals

    check(next(iter(obtain_frame())))


@pytest.mark.skipif(IS_PY313_0 or IS_PY313_1, reason="Crashes on Python 3.13.0")
def test_evaluate_expression_5(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    def check(frame):
        eval_txt = "A, B = 5, 6"
        evaluate_expression(None, frame, eval_txt, is_exec=True)
        assert frame.f_locals["A"] == 5
        assert frame.f_locals["B"] == 6

    check(next(iter(obtain_frame())))


class _DummyPyDB(object):
    def __init__(self):
        self.created_pydb_daemon_threads = {}
        self.timeout_tracker = NULL
        self.multi_threads_single_notification = False


try:
    from ast import PyCF_ALLOW_TOP_LEVEL_AWAIT  # @UnusedImport

    CAN_EVALUATE_TOP_LEVEL_ASYNC = True
except:
    CAN_EVALUATE_TOP_LEVEL_ASYNC = False


@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC, reason="Requires top-level async evaluation.")
def test_evaluate_expression_async_exec(disable_critical_log):
    py_db = _DummyPyDB()

    async def async_call(a):
        return a

    async def main():
        from _pydevd_bundle.pydevd_vars import evaluate_expression

        a = 10
        assert async_call is not None  # Make sure it's in the locals.
        frame = sys._getframe()
        eval_txt = "y = await async_call(a)"
        evaluate_expression(py_db, frame, eval_txt, is_exec=True)
        assert frame.f_locals["y"] == a

    import asyncio

    asyncio.run(main())


@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC, reason="Requires top-level async evaluation.")
def test_evaluate_expression_async_exec_as_eval(disable_critical_log):
    py_db = _DummyPyDB()

    async def async_call(a):
        return a

    async def main():
        from _pydevd_bundle.pydevd_vars import evaluate_expression

        assert async_call is not None  # Make sure it's in the locals.
        frame = sys._getframe()
        eval_txt = "await async_call(10)"
        from io import StringIO

        _original_stdout = sys.stdout
        try:
            stringio = sys.stdout = StringIO()
            evaluate_expression(py_db, frame, eval_txt, is_exec=True)
        finally:
            sys.stdout = _original_stdout

        # I.e.: Check that we printed the value obtained in the exec.
        assert "10\n" in stringio.getvalue()

    import asyncio

    asyncio.run(main())


@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC or IS_PY313_0, reason="Requires top-level async evaluation. Crashes on Python 3.13.0")
def test_evaluate_expression_async_exec_error(disable_critical_log):
    py_db = _DummyPyDB()

    async def async_call(a):
        raise RuntimeError("foobar")

    async def main():
        from _pydevd_bundle.pydevd_vars import evaluate_expression

        assert async_call is not None  # Make sure it's in the locals.
        frame = sys._getframe()
        eval_txt = "y = await async_call(10)"
        with pytest.raises(RuntimeError) as e:
            evaluate_expression(py_db, frame, eval_txt, is_exec=True)
            assert "foobar" in str(e)
        assert "y" not in frame.f_locals

    import asyncio

    asyncio.run(main())


@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC or IS_PY313_0, reason="Requires top-level async evaluation. Crashes on Python 3.13.0")
def test_evaluate_expression_async_eval(disable_critical_log):
    py_db = _DummyPyDB()

    async def async_call(a):
        return a

    async def main():
        from _pydevd_bundle.pydevd_vars import evaluate_expression

        a = 10
        assert async_call is not None  # Make sure it's in the locals.
        frame = sys._getframe()
        eval_txt = "await async_call(a)"
        v = evaluate_expression(py_db, frame, eval_txt, is_exec=False)
        if isinstance(v, ExceptionOnEvaluate):
            raise v.result.with_traceback(v.tb)
        assert v == a

    import asyncio

    asyncio.run(main())


@pytest.mark.skipif(not CAN_EVALUATE_TOP_LEVEL_ASYNC or IS_PY313_0, reason="Requires top-level async evaluation. Crashes on Python 3.13.0")
def test_evaluate_expression_async_eval_error(disable_critical_log):
    py_db = _DummyPyDB()

    async def async_call(a):
        raise RuntimeError("foobar")

    async def main():
        from _pydevd_bundle.pydevd_vars import evaluate_expression

        a = 10
        assert async_call is not None  # Make sure it's in the locals.
        frame = sys._getframe()
        eval_txt = "await async_call(a)"
        v = evaluate_expression(py_db, frame, eval_txt, is_exec=False)
        assert isinstance(v, ExceptionOnEvaluate)
        assert "foobar" in str(v.result)

    import asyncio

    asyncio.run(main())


def test_evaluate_expression_name_mangling(disable_critical_log):
    from _pydevd_bundle.pydevd_vars import evaluate_expression

    class SomeObj(object):
        def __init__(self):
            self.__value = 10
            self.frame = sys._getframe()

    obj = SomeObj()
    frame = obj.frame

    eval_txt = """self.__value"""
    v = evaluate_expression(None, frame, eval_txt, is_exec=False)
    if isinstance(v, ExceptionOnEvaluate):
        raise v.result.with_traceback(v.tb)

    assert v == 10