Skip to content

Commit 78f9938

Browse files
do distinguish between dict . and [] operators
1 parent df32a3c commit 78f9938

File tree

3 files changed

+41
-14
lines changed

3 files changed

+41
-14
lines changed

include/JSObjectProxy.hh

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,23 @@ public:
4848
static Py_ssize_t JSObjectProxy_length(JSObjectProxy *self);
4949

5050
/**
51-
* @brief Getter method (.mp_subscript), returns a value from the JSObjectProxy given a key, used by several built-in python methods as well as the [] operator
51+
* @brief Getter method, returns a value from the JSObjectProxy given a key, used by several built-in python methods as well as the . operator
5252
*
5353
* @param self - The JSObjectProxy
5454
* @param key - The key for the value in the JSObjectProxy
5555
* @return PyObject* NULL on exception, the corresponding value otherwise
5656
*/
5757
static PyObject *JSObjectProxy_get(JSObjectProxy *self, PyObject *key);
5858

59+
/**
60+
* @brief Getter method (.mp_subscript), returns a value from the JSObjectProxy given a key, used by the [] operator
61+
*
62+
* @param self - The JSObjectProxy
63+
* @param key - The key for the value in the JSObjectProxy
64+
* @return PyObject* NULL on exception, the corresponding value otherwise
65+
*/
66+
static PyObject *JSObjectProxy_get_subscript(JSObjectProxy *self, PyObject *key);
67+
5968
/**
6069
* @brief Test method (.sq_contains), returns whether a key exists, used by the in operator
6170
*
@@ -289,7 +298,7 @@ PyDoc_STRVAR(dict_values__doc__,
289298
*/
290299
static PyMappingMethods JSObjectProxy_mapping_methods = {
291300
.mp_length = (lenfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_length,
292-
.mp_subscript = (binaryfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get,
301+
.mp_subscript = (binaryfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get_subscript,
293302
.mp_ass_subscript = (objobjargproc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign
294303
};
295304

src/JSObjectProxy.cc

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *s
6464
return props.length();
6565
}
6666

67-
static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId id) {
67+
static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId id, bool checkPropertyShadowsMethod) {
6868
// look through the methods for dispatch
6969
for (size_t index = 0;; index++) {
7070
const char *methodName = JSObjectProxyType.tp_methods[index].ml_name;
@@ -111,14 +111,16 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId
111111
}
112112
else {
113113
if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) {
114-
// just make sure no property is shadowing a method by name
115-
JS::RootedValue value(GLOBAL_CX);
116-
JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value);
117-
if (!value.isUndefined()) {
118-
return pyTypeFactory(GLOBAL_CX, value)->getPyObject();
119-
} else {
120-
return PyObject_GenericGetAttr((PyObject *)self, key);
114+
if (checkPropertyShadowsMethod) {
115+
// just make sure no property is shadowing a method by name
116+
JS::RootedValue value(GLOBAL_CX);
117+
JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value);
118+
if (!value.isUndefined()) {
119+
return pyTypeFactory(GLOBAL_CX, value)->getPyObject();
120+
}
121121
}
122+
123+
return PyObject_GenericGetAttr((PyObject *)self, key);
122124
}
123125
}
124126
}
@@ -132,7 +134,18 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self,
132134
return NULL;
133135
}
134136

135-
return getKey(self, key, id);
137+
return getKey(self, key, id, false);
138+
}
139+
140+
PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get_subscript(JSObjectProxy *self, PyObject *key)
141+
{
142+
JS::RootedId id(GLOBAL_CX);
143+
if (!keyToId(key, &id)) {
144+
PyErr_SetString(PyExc_AttributeError, "JSObjectProxy property name must be of type str or int");
145+
return NULL;
146+
}
147+
148+
return getKey(self, key, id, true);
136149
}
137150

138151
int JSObjectProxyMethodDefinitions::JSObjectProxy_contains(JSObjectProxy *self, PyObject *key)
@@ -589,7 +602,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method(JSObje
589602
return NULL;
590603
}
591604

592-
PyObject *value = getKey(self, key, id);
605+
PyObject *value = getKey(self, key, id, true);
593606
if (value == Py_None) {
594607
assignKeyValue(self, key, id, default_value);
595608
Py_XINCREF(default_value);

tests/python/test_dict_methods.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ def test_setdefault_no_params():
5555
assert (False)
5656
except Exception as e:
5757
assert str(type(e)) == "<class 'TypeError'>"
58-
assert str(e) == "setdefault expected at least 1 argument, got 0"
58+
assert str(e) == "setdefault expected at least 1 argument, got 0"
59+
60+
def test_setdefault_with_shadowing():
61+
jsObj = pm.eval("({get: 'value'})")
62+
a = jsObj.setdefault("get", "val")
63+
assert a == 'value'
5964

6065
#pop
6166
def test_pop_found():
@@ -453,5 +458,5 @@ def test_get_method():
453458
#get method shadowing
454459
def test_method_shadowing():
455460
jsObj = pm.eval("({get: 'value'})")
456-
assert jsObj.get == 'value'
461+
assert repr(jsObj.get).__contains__("<built-in method get of dict object at")
457462
assert jsObj['get'] == 'value'

0 commit comments

Comments
 (0)