Skip to content

Commit 6f96d26

Browse files
committed
webassembly/variants/pyscript: Enable weakref module and add tests.
The webassembly port needs some additional weakref tests due to the fact that garbage collection only happens when Python execution finishes and JavaScript resumes. The `tests/ports/webassembly/heap_expand.py` expected output also needs to be updated because the amount of GC heap got smaller (weakref WTB takes some of the available RAM). Signed-off-by: Damien George <damien@micropython.org>
1 parent f83f363 commit 6f96d26

9 files changed

Lines changed: 227 additions & 23 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_FULL_FEATURES)
22
#define MICROPY_GC_SPLIT_HEAP (1)
33
#define MICROPY_GC_SPLIT_HEAP_AUTO (1)
4+
#define MICROPY_PY_WEAKREF (1)

tests/basics/weakref_finalize_collect.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Test weakref.finalize() functionality requiring gc.collect().
2+
# Should be kept in sync with tests/ports/webassembly/weakref_finalize_collect.py.
23

34
try:
45
import weakref

tests/basics/weakref_multiple_refs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ def __str__(self):
1919

2020

2121
def test():
22+
global r1, r2 # needed for webassembly port to retain references to them
23+
2224
print("test having multiple ref and finalize objects referencing the same thing")
2325
a = A()
2426
r1 = weakref.ref(a, lambda r: print("ref1", r()))

tests/basics/weakref_ref_collect.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Test weakref.ref() functionality requiring gc.collect().
2+
# Should be kept in sync with tests/ports/webassembly/weakref_ref_collect.py.
23

34
try:
45
import weakref

tests/ports/webassembly/heap_expand.mjs.exp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
135241312
2-
135241280
3-
135241248
4-
135241216
5-
135241168
6-
135241120
7-
135241040
8-
135240896
9-
135240592
10-
135240064
11-
135239024
12-
135236960
1+
135233568
2+
135233536
3+
135233504
4+
135233472
5+
135233424
6+
135233376
7+
135233296
8+
135233152
139
135232848
14-
135224640
15-
135208240
16-
135175456
17-
135109840
18-
134978752
19-
134716592
20-
135216800
21-
136217168
22-
138217984
23-
142219568
24-
150222816
10+
135232320
11+
135231280
12+
135229216
13+
135225104
14+
135216896
15+
135200496
16+
135167712
17+
135102096
18+
134971008
19+
134708848
20+
135201312
21+
136186256
22+
138156160
23+
142095984
24+
149975648
2525
1
2626
2
2727
4
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Test weakref.finalize() functionality requiring gc.collect().
2+
// Should be kept in sync with tests/basics/weakref_finalize_collect.py.
3+
//
4+
// This needs custom testing on the webassembly port since the GC can only
5+
// run when Python code returns to JavaScript.
6+
7+
const mp = await (await import(process.argv[2])).loadMicroPython();
8+
9+
// Set up.
10+
mp.runPython(`
11+
import gc, weakref
12+
13+
class A:
14+
def __str__(self):
15+
return "<A object>"
16+
17+
def callback(*args, **kwargs):
18+
print("callback({}, {})".format(args, kwargs))
19+
return 42
20+
`);
21+
22+
console.log("test basic use of finalize() with a simple callback");
23+
mp.runPython(`
24+
a = A()
25+
f = weakref.finalize(a, callback)
26+
a = None
27+
gc.collect()
28+
`);
29+
console.log("(outside Python)");
30+
mp.runPython(`
31+
print("alive", f.alive)
32+
print("peek", f.peek())
33+
print("detach", f.detach())
34+
print("call", f())
35+
`);
36+
37+
console.log("test that a callback is passed the correct values");
38+
mp.runPython(`
39+
a = A()
40+
f = weakref.finalize(a, callback, 1, 2, kwarg=3)
41+
a = None
42+
gc.collect()
43+
`);
44+
console.log("(outside Python)");
45+
mp.runPython(`
46+
print("alive", f.alive)
47+
print("peek", f.peek())
48+
print("detach", f.detach())
49+
print("call", f())
50+
`);
51+
52+
console.log("test that calling the finalizer cancels the finalizer");
53+
mp.runPython(`
54+
a = A()
55+
f = weakref.finalize(a, callback)
56+
print(f())
57+
print(a)
58+
a = None
59+
gc.collect()
60+
`);
61+
console.log("(outside Python)");
62+
63+
console.log("test that calling detach cancels the finalizer");
64+
mp.runPython(`
65+
a = A()
66+
f = weakref.finalize(a, callback)
67+
print(len(f.detach()))
68+
print(a)
69+
a = None
70+
gc.collect()
71+
`);
72+
console.log("(outside Python)");
73+
74+
console.log("test that finalize does not get collected before its ref does");
75+
mp.runPython(`
76+
a = A()
77+
weakref.finalize(a, callback)
78+
gc.collect()
79+
`);
80+
console.log("(outside Python)");
81+
mp.runPython(`
82+
print("free a")
83+
a = None
84+
gc.collect()
85+
`);
86+
console.log("(outside Python)");
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
test basic use of finalize() with a simple callback
2+
callback((), {})
3+
(outside Python)
4+
alive False
5+
peek None
6+
detach None
7+
call None
8+
test that a callback is passed the correct values
9+
callback((1, 2), {'kwarg': 3})
10+
(outside Python)
11+
alive False
12+
peek None
13+
detach None
14+
call None
15+
test that calling the finalizer cancels the finalizer
16+
callback((), {})
17+
42
18+
<A object>
19+
(outside Python)
20+
test that calling detach cancels the finalizer
21+
4
22+
<A object>
23+
(outside Python)
24+
test that finalize does not get collected before its ref does
25+
(outside Python)
26+
free a
27+
callback((), {})
28+
(outside Python)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Test weakref.ref() functionality requiring gc.collect().
2+
// Should be kept in sync with tests/basics/weakref_ref_collect.py.
3+
//
4+
// This needs custom testing on the webassembly port since the GC can only
5+
// run when Python code returns to JavaScript.
6+
7+
const mp = await (await import(process.argv[2])).loadMicroPython();
8+
9+
// Set up.
10+
mp.runPython(`
11+
import gc, weakref
12+
13+
class A:
14+
def __str__(self):
15+
return "<A object>"
16+
17+
def callback(r):
18+
print("callback", r())
19+
`);
20+
21+
console.log("test basic use of ref() with only one argument");
22+
mp.runPython(`
23+
a = A()
24+
r = weakref.ref(a)
25+
print(r())
26+
gc.collect()
27+
`);
28+
console.log("(outside Python)");
29+
mp.runPython(`
30+
print(r())
31+
a = None
32+
gc.collect()
33+
`);
34+
console.log("(outside Python)");
35+
mp.runPython(`
36+
print(r())
37+
`);
38+
39+
console.log("test use of ref() with a callback");
40+
mp.runPython(`
41+
a = A()
42+
r = weakref.ref(a, callback)
43+
print(r())
44+
gc.collect()
45+
`);
46+
console.log("(outside Python)");
47+
mp.runPython(`
48+
print(r())
49+
a = None
50+
gc.collect()
51+
`);
52+
console.log("(outside Python)");
53+
mp.runPython(`
54+
print(r())
55+
`);
56+
57+
console.log("test when weakref gets collected before the object it refs");
58+
mp.runPython(`
59+
a = A()
60+
r = weakref.ref(a, callback)
61+
print(r())
62+
r = None
63+
gc.collect()
64+
`);
65+
console.log("(outside Python)");
66+
mp.runPython(`
67+
a = None
68+
gc.collect()
69+
`);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
test basic use of ref() with only one argument
2+
<A object>
3+
(outside Python)
4+
<A object>
5+
(outside Python)
6+
None
7+
test use of ref() with a callback
8+
<A object>
9+
(outside Python)
10+
<A object>
11+
callback None
12+
(outside Python)
13+
None
14+
test when weakref gets collected before the object it refs
15+
<A object>
16+
(outside Python)

0 commit comments

Comments
 (0)