Skip to content

Commit 278cfe8

Browse files
authored
Merge pull request #42 from Distributive-Network/bugfix/deserialize-arguments
Add unwrapping for constructors and functions
2 parents f776f1f + 56ea4fa commit 278cfe8

3 files changed

Lines changed: 107 additions & 2 deletions

File tree

dcp/dry/class_manager.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ def js_ref_generator(self, *args, **kwargs):
101101
# otherwise, instantiate a new underlying js ref using the ctor args
102102
else:
103103
async_wrapped_ctor = blockify(pm.new(js_class))
104-
self.js_ref = async_wrapped_ctor(*args, **kwargs)
104+
# If constructor takes other BF2 objects, the underlying JS proxy must be passed instead
105+
unwrapped_args = tuple(arg.js_ref if hasattr(arg, 'js_ref') else arg for arg in args)
106+
self.js_ref = async_wrapped_ctor(*unwrapped_args, **kwargs)
105107
return self.js_ref
106108

107109
return make_new_class(js_ref_generator, name, js_class=js_class)
@@ -120,6 +122,10 @@ def wrap_obj(js_val):
120122
bfclass = reg.add(bfclass)
121123

122124
return bfclass(js_val)
125+
126+
# TODO: Arrays from bf2 objects are returned UNWRAPPED as pm.JSArrayProxy
127+
# TODO: This causes errors when you call an async function within an array
128+
# TODO: To solve this we likely have to write a bf2 array wrapper?
123129
return js_val
124130

125131
# TODO: there must be a better way to check for this...

dcp/initialization.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ def fn_wrapper(*args, **kwargs):
3737
for arg in args:
3838
if js.utils.throws_in_pm(arg):
3939
raise Exception(f'{type(arg)} is not supported in PythonMonkey')
40-
ret_val = aio.blockify(prop_ref)(*args, **kwargs)
40+
# If function takes BF2 objects, the underlying JS proxy must be passed instead
41+
unwrapped_args = tuple(arg.js_ref if hasattr(arg, 'js_ref') else arg for arg in args)
42+
ret_val = aio.blockify(prop_ref)(*unwrapped_args, **kwargs)
4143
return _wrap_js('dynamically_accessed_property', ret_val)
4244
return fn_wrapper
4345

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import unittest
2+
import pythonmonkey as pm
3+
from dcp.initialization import _wrap_js
4+
5+
JSCookieJar = pm.eval("""
6+
class CookieJar
7+
{
8+
constructor(...cookies)
9+
{
10+
this.cookies = []
11+
for (var cookie of cookies) {
12+
this.addCookie(cookie)
13+
}
14+
}
15+
addCookie(cookie)
16+
{
17+
this.cookies.push(cookie)
18+
}
19+
topCookie()
20+
{
21+
return this.cookies.at(-1)
22+
}
23+
}
24+
CookieJar;
25+
""")
26+
27+
JSCookie = pm.eval("""
28+
class Cookie
29+
{
30+
constructor(flavor)
31+
{
32+
this.flavor = flavor
33+
}
34+
}
35+
Cookie;
36+
""")
37+
38+
PyCookie = _wrap_js("Cookie", JSCookie)
39+
PyCookieJar = _wrap_js("CookieJar", JSCookieJar)
40+
41+
42+
class TestObjectUnwrapping(unittest.TestCase):
43+
44+
def test_primitive_args_func(self):
45+
# ensure wrapped functions return correct primitives
46+
bf_fn = _wrap_js('test_fn', pm.eval('(x) => x'))
47+
self.assertEqual(bf_fn("Hello"), "Hello")
48+
self.assertEqual(type(bf_fn(7)), float)
49+
50+
def test_bifrost_args_func(self):
51+
# object must be unwrapped before being passed to pm
52+
bf_fn = _wrap_js('test_fn', pm.eval('(cookie) => cookie instanceof Cookie'))
53+
cookie = PyCookie('chocolate chip')
54+
self.assertTrue(bf_fn(cookie))
55+
56+
# returned object must be wrapped
57+
bf_fn = _wrap_js('test_fn', pm.eval('(cookie) => cookie'))
58+
cookie = PyCookie('chocolate chip')
59+
self.assertEqual(type(bf_fn(cookie)), PyCookie)
60+
61+
def test_primitive_args_ctor(self):
62+
63+
cookie = PyCookie('chocolate chip')
64+
65+
# Verify object is wrapped properly
66+
self.assertEqual(type(cookie), PyCookie)
67+
self.assertTrue(hasattr(cookie, 'js_ref'))
68+
self.assertEqual(cookie.flavor, 'chocolate chip')
69+
70+
# Verify underlying object is the right class
71+
self.assertTrue(pm.eval('(cookie) => cookie instanceof Cookie')(cookie.js_ref))
72+
73+
def test_bifrost_args_ctor(self):
74+
75+
cookie_one = PyCookie('chocolate chip')
76+
cookie_two = PyCookie('oatmeal')
77+
78+
# Constructors that take bf2 objects must unwrap them
79+
py_jar = PyCookieJar(cookie_one, cookie_two)
80+
81+
# Attributes and obj need to be the correct type when obtained in underlying js
82+
js_fn = pm.eval('(jar) => jar instanceof CookieJar && jar.topCookie() instanceof Cookie')
83+
self.assertTrue(js_fn(py_jar.js_ref))
84+
85+
def test_bifrost_args_method(self):
86+
87+
py_jar = PyCookieJar()
88+
89+
# bf2 objects passed as function arguments must be unwrapped
90+
py_jar.addCookie( PyCookie('chocolate chip') )
91+
js_fn = pm.eval('(jar) => jar.topCookie() instanceof Cookie')
92+
93+
self.assertTrue(js_fn(py_jar.js_ref))
94+
95+
96+
if __name__ == '__main__':
97+
unittest.main()

0 commit comments

Comments
 (0)