rapydscript-ns 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +10 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/README.md +7 -6
- package/TODO.md +116 -1
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +70 -70
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +9 -9
- package/language-service/language-service.d.ts +1 -1
- package/package.json +6 -2
- package/publish.py +37 -37
- package/release/compiler.js +246 -231
- package/release/signatures.json +23 -23
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/aes.pyj +646 -646
- package/src/lib/contextlib.pyj +379 -0
- package/src/lib/copy.pyj +120 -120
- package/src/lib/datetime.pyj +712 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/io.pyj +500 -0
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/json.pyj +227 -0
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/random.pyj +118 -118
- package/src/lib/react.pyj +74 -74
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/diagnostics.js +4 -4
- package/src/monaco-language-service/dts.js +550 -550
- package/src/monaco-language-service/index.js +2 -2
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/jsx.pyj +164 -164
- package/src/output/loops.pyj +9 -0
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +1 -1
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/contextlib.pyj +362 -0
- package/test/datetime.pyj +500 -0
- package/test/debugger_stmt.pyj +41 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/imports.pyj +72 -72
- package/test/internationalization.pyj +73 -73
- package/test/io.pyj +316 -0
- package/test/json.pyj +196 -0
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/omit_function_metadata.pyj +20 -20
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/unit/index.js +66 -0
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +1 -1
- package/test/unit/web-repl.js +533 -0
- package/tools/compiler.d.ts +367 -0
- package/tools/completer.js +131 -131
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +224 -224
- package/web-repl/sha1.js +25 -25
package/test/annotations.pyj
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
def add(a: int, b: float):
|
|
2
|
-
return a + b
|
|
3
|
-
|
|
4
|
-
assrt.ok(add.__annotations__)
|
|
5
|
-
assrt.equal(add.__annotations__['a'], int)
|
|
6
|
-
assrt.equal(add.__annotations__['b'], float)
|
|
7
|
-
assrt.equal(add.__annotations__['return'], undefined)
|
|
8
|
-
|
|
9
|
-
def sum(ls: list) -> int:
|
|
10
|
-
pass
|
|
11
|
-
|
|
12
|
-
assrt.ok(not (sum.__annotations__ == undefined))
|
|
13
|
-
assrt.deepEqual(sum.__annotations__, {
|
|
14
|
-
'ls': list,
|
|
15
|
-
'return': int
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
def optional(a:int=10):
|
|
19
|
-
return a
|
|
20
|
-
|
|
21
|
-
assrt.ok(not (optional.__annotations__ == undefined))
|
|
22
|
-
assrt.equal(optional.__annotations__.a, int)
|
|
23
|
-
assrt.equal(optional.__defaults__.a, 10)
|
|
24
|
-
|
|
25
|
-
def otherexpr(a:3+4) -> [1, 2]:
|
|
26
|
-
pass
|
|
27
|
-
|
|
28
|
-
assrt.ok(not (otherexpr.__annotations__ == undefined))
|
|
29
|
-
assrt.equal(otherexpr.__annotations__['a'], 7)
|
|
30
|
-
assrt.deepEqual(otherexpr.__annotations__['return'], [1, 2])
|
|
31
|
-
|
|
32
|
-
def basic(x:float):
|
|
33
|
-
pass
|
|
34
|
-
|
|
35
|
-
assrt.deepEqual(basic.__annotations__, {
|
|
36
|
-
'x': float
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
def kwstarargs(*args:list, **kwargs:dict) -> int:
|
|
40
|
-
pass
|
|
41
|
-
|
|
42
|
-
assrt.equal(kwstarargs.__annotations__['return'], int)
|
|
43
|
-
|
|
44
|
-
def nothing():
|
|
45
|
-
pass
|
|
46
|
-
|
|
47
|
-
assrt.ok(nothing.__annotations__ == undefined)
|
|
48
|
-
assrt.throws(def():
|
|
49
|
-
nothing.__annotations__['return']
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
test = def(x: int):
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
assrt.deepEqual(test.__annotations__, {
|
|
56
|
-
'x': int
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
anonreturn = def() -> 'test':
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
|
-
assrt.equal(anonreturn.__annotations__['return'], 'test')
|
|
63
|
-
|
|
64
|
-
assrt.equal(def asexpr(a: int):
|
|
65
|
-
a
|
|
66
|
-
.__annotations__['a'], int)
|
|
67
|
-
|
|
68
|
-
assrt.deepEqual(def(a: int) -> float:
|
|
69
|
-
a + 10.0
|
|
70
|
-
.__annotations__, {
|
|
71
|
-
'a': int,
|
|
72
|
-
'return': float
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
class A:
|
|
76
|
-
|
|
77
|
-
def f(self, a : int, b: 'x') -> float:
|
|
78
|
-
pass
|
|
79
|
-
|
|
80
|
-
assrt.deepEqual(A.prototype.f.__annotations__, {'a':int, 'b':'x', 'return': float})
|
|
1
|
+
def add(a: int, b: float):
|
|
2
|
+
return a + b
|
|
3
|
+
|
|
4
|
+
assrt.ok(add.__annotations__)
|
|
5
|
+
assrt.equal(add.__annotations__['a'], int)
|
|
6
|
+
assrt.equal(add.__annotations__['b'], float)
|
|
7
|
+
assrt.equal(add.__annotations__['return'], undefined)
|
|
8
|
+
|
|
9
|
+
def sum(ls: list) -> int:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
assrt.ok(not (sum.__annotations__ == undefined))
|
|
13
|
+
assrt.deepEqual(sum.__annotations__, {
|
|
14
|
+
'ls': list,
|
|
15
|
+
'return': int
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
def optional(a:int=10):
|
|
19
|
+
return a
|
|
20
|
+
|
|
21
|
+
assrt.ok(not (optional.__annotations__ == undefined))
|
|
22
|
+
assrt.equal(optional.__annotations__.a, int)
|
|
23
|
+
assrt.equal(optional.__defaults__.a, 10)
|
|
24
|
+
|
|
25
|
+
def otherexpr(a:3+4) -> [1, 2]:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
assrt.ok(not (otherexpr.__annotations__ == undefined))
|
|
29
|
+
assrt.equal(otherexpr.__annotations__['a'], 7)
|
|
30
|
+
assrt.deepEqual(otherexpr.__annotations__['return'], [1, 2])
|
|
31
|
+
|
|
32
|
+
def basic(x:float):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
assrt.deepEqual(basic.__annotations__, {
|
|
36
|
+
'x': float
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
def kwstarargs(*args:list, **kwargs:dict) -> int:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
assrt.equal(kwstarargs.__annotations__['return'], int)
|
|
43
|
+
|
|
44
|
+
def nothing():
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
assrt.ok(nothing.__annotations__ == undefined)
|
|
48
|
+
assrt.throws(def():
|
|
49
|
+
nothing.__annotations__['return']
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
test = def(x: int):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
assrt.deepEqual(test.__annotations__, {
|
|
56
|
+
'x': int
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
anonreturn = def() -> 'test':
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
assrt.equal(anonreturn.__annotations__['return'], 'test')
|
|
63
|
+
|
|
64
|
+
assrt.equal(def asexpr(a: int):
|
|
65
|
+
a
|
|
66
|
+
.__annotations__['a'], int)
|
|
67
|
+
|
|
68
|
+
assrt.deepEqual(def(a: int) -> float:
|
|
69
|
+
a + 10.0
|
|
70
|
+
.__annotations__, {
|
|
71
|
+
'a': int,
|
|
72
|
+
'return': float
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
class A:
|
|
76
|
+
|
|
77
|
+
def f(self, a : int, b: 'x') -> float:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
assrt.deepEqual(A.prototype.f.__annotations__, {'a':int, 'b':'x', 'return': float})
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# globals: assrt
|
|
2
|
+
# vim:fileencoding=utf-8
|
|
3
|
+
#
|
|
4
|
+
# contextlib.pyj — tests for the contextlib standard library module.
|
|
5
|
+
|
|
6
|
+
ae = assrt.equal
|
|
7
|
+
ade = assrt.deepEqual
|
|
8
|
+
ok = assrt.ok
|
|
9
|
+
|
|
10
|
+
from contextlib import (
|
|
11
|
+
AbstractContextManager, contextmanager, closing,
|
|
12
|
+
nullcontext, suppress, ExitStack
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ── 1. AbstractContextManager ─────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
class _SimpleCM(AbstractContextManager):
|
|
19
|
+
def __init__(self):
|
|
20
|
+
self.exited = False
|
|
21
|
+
self.exc_type = None
|
|
22
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
23
|
+
self.exited = True
|
|
24
|
+
self.exc_type = exc_type
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
_cm = _SimpleCM()
|
|
28
|
+
with _cm as val:
|
|
29
|
+
ae(val, _cm) # __enter__ returns self
|
|
30
|
+
|
|
31
|
+
ok(_cm.exited)
|
|
32
|
+
ae(_cm.exc_type, None) # no exception
|
|
33
|
+
|
|
34
|
+
# AbstractContextManager with default __exit__ (no-op)
|
|
35
|
+
class _DefaultExit(AbstractContextManager):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
_de = _DefaultExit()
|
|
39
|
+
with _de as v:
|
|
40
|
+
ae(v, _de)
|
|
41
|
+
|
|
42
|
+
# ── 2. closing ────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
class _Closeable:
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self.closed = False
|
|
47
|
+
def close(self):
|
|
48
|
+
self.closed = True
|
|
49
|
+
|
|
50
|
+
_obj = _Closeable()
|
|
51
|
+
with closing(_obj) as c:
|
|
52
|
+
ae(c, _obj)
|
|
53
|
+
ok(not c.closed)
|
|
54
|
+
ok(_obj.closed) # close() called on exit
|
|
55
|
+
|
|
56
|
+
# close() is called even when an exception occurs
|
|
57
|
+
_obj2 = _Closeable()
|
|
58
|
+
_raised = False
|
|
59
|
+
try:
|
|
60
|
+
with closing(_obj2):
|
|
61
|
+
raise ValueError('oops')
|
|
62
|
+
except ValueError:
|
|
63
|
+
_raised = True
|
|
64
|
+
ok(_raised)
|
|
65
|
+
ok(_obj2.closed) # still closed despite exception
|
|
66
|
+
|
|
67
|
+
# ── 3. nullcontext ────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
with nullcontext() as v:
|
|
70
|
+
ae(v, None)
|
|
71
|
+
|
|
72
|
+
with nullcontext(42) as v:
|
|
73
|
+
ae(v, 42)
|
|
74
|
+
|
|
75
|
+
with nullcontext('hello') as s:
|
|
76
|
+
ae(s, 'hello')
|
|
77
|
+
|
|
78
|
+
# ── 4. suppress ───────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
# Suppress a matching exception — execution continues after the with block
|
|
81
|
+
_reached = False
|
|
82
|
+
with suppress(ValueError):
|
|
83
|
+
raise ValueError('ignored')
|
|
84
|
+
_reached = True
|
|
85
|
+
ok(_reached)
|
|
86
|
+
|
|
87
|
+
# Suppress one of several listed types
|
|
88
|
+
_reached2 = False
|
|
89
|
+
with suppress(KeyError, ValueError):
|
|
90
|
+
raise KeyError('ignored')
|
|
91
|
+
_reached2 = True
|
|
92
|
+
ok(_reached2)
|
|
93
|
+
|
|
94
|
+
# Non-matching exception propagates
|
|
95
|
+
_non_match = False
|
|
96
|
+
try:
|
|
97
|
+
with suppress(KeyError):
|
|
98
|
+
raise ValueError('not suppressed')
|
|
99
|
+
except ValueError:
|
|
100
|
+
_non_match = True
|
|
101
|
+
ok(_non_match)
|
|
102
|
+
|
|
103
|
+
# No exception — block runs normally
|
|
104
|
+
_no_exc = False
|
|
105
|
+
with suppress(ValueError):
|
|
106
|
+
_no_exc = True
|
|
107
|
+
ok(_no_exc)
|
|
108
|
+
|
|
109
|
+
# suppress returns self from __enter__
|
|
110
|
+
with suppress(TypeError) as ctx:
|
|
111
|
+
ok(ctx is not None)
|
|
112
|
+
|
|
113
|
+
# ── 5. contextmanager ─────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
# Basic: yield value becomes the `as` target
|
|
116
|
+
@contextmanager
|
|
117
|
+
def _cm_basic(val):
|
|
118
|
+
yield val
|
|
119
|
+
|
|
120
|
+
with _cm_basic(99) as v:
|
|
121
|
+
ae(v, 99)
|
|
122
|
+
|
|
123
|
+
# setup/teardown order
|
|
124
|
+
_log = []
|
|
125
|
+
|
|
126
|
+
@contextmanager
|
|
127
|
+
def _cm_log(name):
|
|
128
|
+
_log.push('enter:' + name)
|
|
129
|
+
try:
|
|
130
|
+
yield name
|
|
131
|
+
finally:
|
|
132
|
+
_log.push('exit:' + name)
|
|
133
|
+
|
|
134
|
+
with _cm_log('x') as v:
|
|
135
|
+
ae(v, 'x')
|
|
136
|
+
_log.push('body')
|
|
137
|
+
|
|
138
|
+
ade(_log, ['enter:x', 'body', 'exit:x'])
|
|
139
|
+
|
|
140
|
+
# exception in the body is thrown into the generator and re-raised if not caught
|
|
141
|
+
_teardown_ran = False
|
|
142
|
+
_body_exc_caught = False
|
|
143
|
+
|
|
144
|
+
@contextmanager
|
|
145
|
+
def _cm_reraise():
|
|
146
|
+
global _teardown_ran
|
|
147
|
+
try:
|
|
148
|
+
yield 'val'
|
|
149
|
+
finally:
|
|
150
|
+
_teardown_ran = True
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
with _cm_reraise():
|
|
154
|
+
raise ValueError('oops')
|
|
155
|
+
except ValueError:
|
|
156
|
+
_body_exc_caught = True
|
|
157
|
+
ok(_teardown_ran)
|
|
158
|
+
ok(_body_exc_caught)
|
|
159
|
+
|
|
160
|
+
# generator catches the exception and suppresses it (returns True from __exit__)
|
|
161
|
+
_suppress_log = []
|
|
162
|
+
|
|
163
|
+
@contextmanager
|
|
164
|
+
def _cm_suppress_exc():
|
|
165
|
+
try:
|
|
166
|
+
yield
|
|
167
|
+
except ValueError:
|
|
168
|
+
_suppress_log.push('caught ValueError')
|
|
169
|
+
|
|
170
|
+
_after_suppress = False
|
|
171
|
+
with _cm_suppress_exc():
|
|
172
|
+
raise ValueError('caught by generator')
|
|
173
|
+
_after_suppress = True
|
|
174
|
+
ok(_after_suppress)
|
|
175
|
+
ade(_suppress_log, ['caught ValueError'])
|
|
176
|
+
|
|
177
|
+
# contextmanager with setup / cleanup and no yield value
|
|
178
|
+
_setup_done = False
|
|
179
|
+
_cleanup_done = False
|
|
180
|
+
|
|
181
|
+
@contextmanager
|
|
182
|
+
def _cm_setup_cleanup():
|
|
183
|
+
global _setup_done, _cleanup_done
|
|
184
|
+
_setup_done = True
|
|
185
|
+
yield
|
|
186
|
+
_cleanup_done = True
|
|
187
|
+
|
|
188
|
+
with _cm_setup_cleanup():
|
|
189
|
+
ok(_setup_done)
|
|
190
|
+
|
|
191
|
+
ok(_cleanup_done)
|
|
192
|
+
|
|
193
|
+
# nested contextmanagers
|
|
194
|
+
_nest_log = []
|
|
195
|
+
|
|
196
|
+
@contextmanager
|
|
197
|
+
def _cm_nest(label):
|
|
198
|
+
_nest_log.push('in:' + label)
|
|
199
|
+
yield label
|
|
200
|
+
_nest_log.push('out:' + label)
|
|
201
|
+
|
|
202
|
+
with _cm_nest('a') as a:
|
|
203
|
+
with _cm_nest('b') as b:
|
|
204
|
+
ae(a, 'a')
|
|
205
|
+
ae(b, 'b')
|
|
206
|
+
|
|
207
|
+
ade(_nest_log, ['in:a', 'in:b', 'out:b', 'out:a'])
|
|
208
|
+
|
|
209
|
+
# ── 6. ExitStack ──────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
# Basic usage as a context manager
|
|
212
|
+
_es_entered = False
|
|
213
|
+
_es_exited = False
|
|
214
|
+
|
|
215
|
+
class _TrackedCM:
|
|
216
|
+
def __init__(self, name):
|
|
217
|
+
self.name = name
|
|
218
|
+
self.entered = False
|
|
219
|
+
self.exited = False
|
|
220
|
+
def __enter__(self):
|
|
221
|
+
self.entered = True
|
|
222
|
+
return self.name
|
|
223
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
224
|
+
self.exited = True
|
|
225
|
+
return False
|
|
226
|
+
|
|
227
|
+
_cm_a = _TrackedCM('a')
|
|
228
|
+
_cm_b = _TrackedCM('b')
|
|
229
|
+
|
|
230
|
+
with ExitStack() as stack:
|
|
231
|
+
va = stack.enter_context(_cm_a)
|
|
232
|
+
vb = stack.enter_context(_cm_b)
|
|
233
|
+
ae(va, 'a')
|
|
234
|
+
ae(vb, 'b')
|
|
235
|
+
ok(_cm_a.entered)
|
|
236
|
+
ok(_cm_b.entered)
|
|
237
|
+
|
|
238
|
+
ok(_cm_a.exited)
|
|
239
|
+
ok(_cm_b.exited)
|
|
240
|
+
|
|
241
|
+
# LIFO exit order
|
|
242
|
+
_exit_order = []
|
|
243
|
+
|
|
244
|
+
class _OrderedCM:
|
|
245
|
+
def __init__(self, label):
|
|
246
|
+
self.label = label
|
|
247
|
+
def __enter__(self):
|
|
248
|
+
return self
|
|
249
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
250
|
+
_exit_order.push(self.label)
|
|
251
|
+
return False
|
|
252
|
+
|
|
253
|
+
with ExitStack() as stack:
|
|
254
|
+
stack.enter_context(_OrderedCM('first'))
|
|
255
|
+
stack.enter_context(_OrderedCM('second'))
|
|
256
|
+
stack.enter_context(_OrderedCM('third'))
|
|
257
|
+
|
|
258
|
+
ade(_exit_order, ['third', 'second', 'first'])
|
|
259
|
+
|
|
260
|
+
# callback() is called on exit
|
|
261
|
+
_cb_called = False
|
|
262
|
+
_cb_arg = None
|
|
263
|
+
|
|
264
|
+
def _my_callback(x):
|
|
265
|
+
global _cb_called, _cb_arg
|
|
266
|
+
_cb_called = True
|
|
267
|
+
_cb_arg = x
|
|
268
|
+
|
|
269
|
+
with ExitStack() as stack:
|
|
270
|
+
stack.callback(_my_callback, 'hello')
|
|
271
|
+
|
|
272
|
+
ok(_cb_called)
|
|
273
|
+
ae(_cb_arg, 'hello')
|
|
274
|
+
|
|
275
|
+
# push() with a raw callable
|
|
276
|
+
_push_called = False
|
|
277
|
+
|
|
278
|
+
def _raw_exit(exc_type=None, exc_val=None, exc_tb=None):
|
|
279
|
+
global _push_called
|
|
280
|
+
_push_called = True
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
with ExitStack() as stack:
|
|
284
|
+
stack.push(_raw_exit)
|
|
285
|
+
|
|
286
|
+
ok(_push_called)
|
|
287
|
+
|
|
288
|
+
# push() with a context manager object (does not call __enter__)
|
|
289
|
+
_push_cm = _TrackedCM('pushed')
|
|
290
|
+
with ExitStack() as stack:
|
|
291
|
+
stack.push(_push_cm)
|
|
292
|
+
|
|
293
|
+
ok(not _push_cm.entered) # __enter__ was NOT called by push()
|
|
294
|
+
ok(_push_cm.exited) # __exit__ WAS called
|
|
295
|
+
|
|
296
|
+
# exception in the with block is passed to registered __exit__ methods
|
|
297
|
+
_exc_saw_exc = False
|
|
298
|
+
|
|
299
|
+
class _SeeExc:
|
|
300
|
+
def __enter__(self): return self
|
|
301
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
302
|
+
global _exc_saw_exc
|
|
303
|
+
if exc_type is not None:
|
|
304
|
+
_exc_saw_exc = True
|
|
305
|
+
return False # don't suppress
|
|
306
|
+
|
|
307
|
+
_exc_propagated = False
|
|
308
|
+
try:
|
|
309
|
+
with ExitStack() as stack:
|
|
310
|
+
stack.enter_context(_SeeExc())
|
|
311
|
+
raise ValueError('test')
|
|
312
|
+
except ValueError:
|
|
313
|
+
_exc_propagated = True
|
|
314
|
+
|
|
315
|
+
ok(_exc_saw_exc)
|
|
316
|
+
ok(_exc_propagated)
|
|
317
|
+
|
|
318
|
+
# a cleanup that suppresses the exception
|
|
319
|
+
_suppressed_by_stack = False
|
|
320
|
+
|
|
321
|
+
class _SuppressCM:
|
|
322
|
+
def __enter__(self): return self
|
|
323
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
324
|
+
return True # suppress whatever is active
|
|
325
|
+
|
|
326
|
+
_after_stack_suppress = False
|
|
327
|
+
with ExitStack() as stack:
|
|
328
|
+
stack.enter_context(_SuppressCM())
|
|
329
|
+
raise KeyError('suppressed by stack')
|
|
330
|
+
_after_stack_suppress = True
|
|
331
|
+
ok(_after_stack_suppress)
|
|
332
|
+
|
|
333
|
+
# close() works outside of a `with` block
|
|
334
|
+
_close_log = []
|
|
335
|
+
|
|
336
|
+
class _CloseTracked:
|
|
337
|
+
def __enter__(self): return self
|
|
338
|
+
def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
|
|
339
|
+
_close_log.push('closed')
|
|
340
|
+
return False
|
|
341
|
+
|
|
342
|
+
stack2 = ExitStack()
|
|
343
|
+
stack2.enter_context(_CloseTracked())
|
|
344
|
+
stack2.close()
|
|
345
|
+
ade(_close_log, ['closed'])
|
|
346
|
+
|
|
347
|
+
# ── 7. Multiple context managers in one `with` statement ─────────────────────
|
|
348
|
+
|
|
349
|
+
# This exercises the compiler's multi-clause `with` support alongside our CMs
|
|
350
|
+
_multi_log = []
|
|
351
|
+
|
|
352
|
+
@contextmanager
|
|
353
|
+
def _cm_multi(label):
|
|
354
|
+
_multi_log.push('enter:' + label)
|
|
355
|
+
yield label
|
|
356
|
+
_multi_log.push('exit:' + label)
|
|
357
|
+
|
|
358
|
+
with _cm_multi('p') as p, _cm_multi('q') as q:
|
|
359
|
+
ae(p, 'p')
|
|
360
|
+
ae(q, 'q')
|
|
361
|
+
|
|
362
|
+
ade(_multi_log, ['enter:p', 'enter:q', 'exit:q', 'exit:p'])
|