Skip to content

Commit ad20d9e

Browse files
authored
Merge pull request #192 from Distributive-Network/philippe/fix/191
JSObjectProxy now implements the dict "or" operation
2 parents 9080ed2 + e3b5632 commit ad20d9e

File tree

4 files changed

+123
-1
lines changed

4 files changed

+123
-1
lines changed

include/JSObjectProxy.hh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,24 @@ public:
129129
* @return the string representation (a PyUnicodeObject) on success, NULL on failure
130130
*/
131131
static PyObject *JSObjectProxy_repr(JSObjectProxy *self);
132+
133+
/**
134+
* @brief Set union method
135+
*
136+
* @param self - The JSObjectProxy
137+
* @param other - The other PyObject to be or'd, expected to be dict or JSObjectProxy
138+
* @return PyObject* The resulting new dict
139+
*/
140+
static PyObject *JSObjectProxy_or(JSObjectProxy *self, PyObject *other);
141+
142+
/**
143+
* @brief Set union method, in place
144+
*
145+
* @param self - The JSObjectProxy
146+
* @param other - The other PyObject to be or'd, expected to be dict or JSObjectProxy
147+
* @return PyObject* The resulting new dict, must be same object as self
148+
*/
149+
static PyObject *JSObjectProxy_ior(JSObjectProxy *self, PyObject *other);
132150
};
133151

134152

@@ -146,6 +164,11 @@ static PySequenceMethods JSObjectProxy_sequence_methods = {
146164
.sq_contains = (objobjproc)JSObjectProxyMethodDefinitions::JSObjectProxy_contains
147165
};
148166

167+
static PyNumberMethods JSObjectProxy_number_methods = {
168+
.nb_or = (binaryfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_or,
169+
.nb_inplace_or = (binaryfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_ior
170+
};
171+
149172
/**
150173
* @brief Struct for the JSObjectProxyType, used by all JSObjectProxy objects
151174
*/

src/JSObjectProxy.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#include <Python.h>
2323

24+
#include <object.h>
25+
2426
JSContext *GLOBAL_CX; /**< pointer to PythonMonkey's JSContext */
2527

2628
bool keyToId(PyObject *key, JS::MutableHandleId idp) {
@@ -258,3 +260,54 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self
258260
PyDict_DelItem(tsDict, cyclicKey);
259261
return str;
260262
}
263+
264+
PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, PyObject *other) {
265+
if (!PyDict_Check(self) || !PyDict_Check(other)) {
266+
Py_RETURN_NOTIMPLEMENTED;
267+
}
268+
269+
if (!PyObject_TypeCheck(self, &JSObjectProxyType) && PyObject_TypeCheck(other, &JSObjectProxyType)) {
270+
return PyDict_Type.tp_as_number->nb_or((PyObject *)&(self->dict), other);
271+
} else {
272+
JS::Rooted<JS::ValueArray<3>> args(GLOBAL_CX);
273+
args[0].setObjectOrNull(JS_NewPlainObject(GLOBAL_CX));
274+
args[1].setObjectOrNull(self->jsObject); // this is null is left operand is real dict
275+
JS::RootedValue jValueOther(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, other));
276+
args[2].setObject(jValueOther.toObject());
277+
278+
JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject));
279+
280+
// call Object.assign
281+
JS::RootedValue Object(GLOBAL_CX);
282+
JS_GetProperty(GLOBAL_CX, *global, "Object", &Object);
283+
284+
JS::RootedObject rootedObject(GLOBAL_CX, Object.toObjectOrNull());
285+
JS::RootedValue ret(GLOBAL_CX);
286+
287+
if (!JS_CallFunctionName(GLOBAL_CX, rootedObject, "assign", args, &ret)) return NULL;
288+
return pyTypeFactory(GLOBAL_CX, global, &ret)->getPyObject();
289+
}
290+
}
291+
292+
PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_ior(JSObjectProxy *self, PyObject *other) {
293+
if (!PyDict_Check(self) || !PyDict_Check(other)) {
294+
Py_RETURN_NOTIMPLEMENTED;
295+
}
296+
297+
JS::Rooted<JS::ValueArray<2>> args(GLOBAL_CX);
298+
args[0].setObjectOrNull(self->jsObject);
299+
JS::RootedValue jValueOther(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, other));
300+
args[1].setObject(jValueOther.toObject());
301+
302+
JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject));
303+
304+
// call Object.assign
305+
JS::RootedValue Object(GLOBAL_CX);
306+
JS_GetProperty(GLOBAL_CX, *global, "Object", &Object);
307+
308+
JS::RootedObject rootedObject(GLOBAL_CX, Object.toObjectOrNull());
309+
JS::RootedValue ret(GLOBAL_CX);
310+
if (!JS_CallFunctionName(GLOBAL_CX, rootedObject, "assign", args, &ret)) return NULL;
311+
Py_INCREF(self);
312+
return (PyObject *)self;
313+
}

src/modules/pythonmonkey/pythonmonkey.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ PyTypeObject JSObjectProxyType = {
7474
.tp_basicsize = sizeof(JSObjectProxy),
7575
.tp_dealloc = (destructor)JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc,
7676
.tp_repr = (reprfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_repr,
77+
.tp_as_number = &JSObjectProxy_number_methods,
7778
.tp_as_sequence = &JSObjectProxy_sequence_methods,
7879
.tp_as_mapping = &JSObjectProxy_mapping_methods,
7980
.tp_getattro = (getattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get,

tests/python/test_dicts_lists.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,49 @@ def test_eval_objects_jsproxy_does_not_contain():
137137

138138
def test_eval_objects_jsproxy_does_not_contain_value():
139139
a = pm.eval("({'c':5})")
140-
assert not(5 in a)
140+
assert not(5 in a)
141+
142+
def test_eval_objects_jsproxy_or():
143+
a = pm.eval("({'c':5})")
144+
b = pm.eval("({'d':6})")
145+
c = a | b
146+
assert a == {'c': 5.0}
147+
assert c == {'c': 5.0, 'd': 6.0}
148+
assert b == {'d': 6.0}
149+
150+
def test_eval_objects_jsproxy_or_true_dict_right():
151+
a = pm.eval("({'c':5})")
152+
b = {'d': 6.0}
153+
c = a | b
154+
assert a == {'c': 5.0}
155+
assert c == {'c': 5.0, 'd': 6.0}
156+
assert b == {'d': 6.0}
157+
158+
def test_eval_objects_jsproxy_or_true_dict_left():
159+
a = {'c':5}
160+
b = pm.eval("({'d':6})")
161+
c = a | b
162+
assert a == {'c': 5.0}
163+
assert c == {'c': 5.0, 'd': 6.0}
164+
assert b == {'d': 6.0}
165+
166+
def test_eval_objects_jsproxy_inplace_or():
167+
a = pm.eval("({'c':5})")
168+
b = pm.eval("({'d':6})")
169+
a |= b
170+
assert a == {'c': 5.0, 'd': 6.0}
171+
assert b == {'d': 6.0}
172+
173+
def test_eval_objects_jsproxy_inplace_or_true_dict_right():
174+
a = pm.eval("({'c':5})")
175+
b = {'d':6.0}
176+
a |= b
177+
assert a == {'c': 5.0, 'd': 6.0}
178+
assert b == {'d': 6.0}
179+
180+
def test_eval_objects_jsproxy_inplace_or_true_dict_left():
181+
a = {'c':5.0}
182+
b = pm.eval("({'d':6})")
183+
a |= b
184+
assert a == {'c': 5.0, 'd': 6.0}
185+
assert b == {'d': 6.0}

0 commit comments

Comments
 (0)