Permalink
Browse files

Merge branch 'jsiters'

Conflicts:
	tests/test-iterate.py
  • Loading branch information...
2 parents 0d2d6d1 + ddb81d4 commit 74291c88fd3474b17901863697ec6af10b8a879c Paul Davis committed May 10, 2009
View
1 THANKS
@@ -2,3 +2,4 @@
Thanks For Patches and Pointers
===============================
benoitc - build system tweaks, compiling on OpenBSD
+Mark Lee - Patch for iterating JS arrays in Python
View
@@ -33,6 +33,12 @@ Array_get_item(Object* self, Py_ssize_t idx)
jsval rval;
jsint pos = (jsint) idx;
+ if(idx >= Array_length(self))
+ {
+ PyErr_SetString(PyExc_IndexError, "List index out of range.");
+ return NULL;
+ }
+
if(!JS_GetElement(self->cx->cx, self->obj, pos, &rval))
{
PyErr_SetString(PyExc_AttributeError, "Failed to get array item.");
@@ -60,6 +66,12 @@ Array_set_item(Object* self, Py_ssize_t idx, PyObject* val)
return 0;
}
+PyObject*
+Array_iterator(Object* self)
+{
+ return PySeqIter_New(self);
+}
+
static PyMemberDef Array_members[] = {
{0, 0, 0, 0}
};
@@ -108,7 +120,7 @@ PyTypeObject _ArrayType = {
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
- 0, /*tp_iter*/
+ (getiterfunc)Array_iterator, /*tp_iter*/
0, /*tp_iternext*/
Array_methods, /*tp_methods*/
Array_members, /*tp_members*/
View
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2009 Paul J. Davis <paul.joseph.davis@gmail.com>
+ *
+ * This file is part of the python-spidermonkey package released
+ * under the MIT license.
+ *
+ */
+
+#include "spidermonkey.h"
+
+PyObject*
+Iterator_Wrap(Context* cx, JSObject* obj)
+{
+ Iterator* self = NULL;
+ PyObject* tpl = NULL;
+ PyObject* ret = NULL;
+ JSObject* iter = NULL;
+ jsval priv;
+
+ // Build our new python object.
+ tpl = Py_BuildValue("(O)", cx);
+ if(tpl == NULL) goto error;
+
+ self = (Iterator*) PyObject_CallObject((PyObject*) IteratorType, tpl);
+ if(self == NULL) goto error;
+
+ // Attach a JS property iterator.
+ self->iter = JS_NewPropertyIterator(cx->cx, obj);
+ if(self->iter == NULL) goto error;
+
+ self->root = OBJECT_TO_JSVAL(self->iter);
+ if(!JS_AddRoot(cx->cx, &(self->root)))
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to set GC root.");
+ goto error;
+ }
+
+ ret = (PyObject*) self;
+ goto success;
+
+error:
+ Py_XDECREF(self);
+ ret = NULL; // In case it was AddRoot
+success:
+ Py_XDECREF(tpl);
+ return (PyObject*) ret;
+}
+
+PyObject*
+Iterator_new(PyTypeObject* type, PyObject* args, PyObject* kwargs)
+{
+ Context* cx = NULL;
+ Iterator* self = NULL;
+
+ if(!PyArg_ParseTuple(args, "O!", ContextType, &cx)) goto error;
+
+ self = (Iterator*) type->tp_alloc(type, 0);
+ if(self == NULL) goto error;
+
+ Py_INCREF(cx);
+ self->cx = cx;
+ self->iter = NULL;
+ goto success;
+
+error:
+ ERROR("spidermonkey.Iterator.new");
+success:
+ return (PyObject*) self;
+}
+
+int
+Iterator_init(Iterator* self, PyObject* args, PyObject* kwargs)
+{
+ return 0;
+}
+
+void
+Iterator_dealloc(Iterator* self)
+{
+ if(self->iter != NULL)
+ {
+ JS_RemoveRoot(self->cx->cx, &(self->root));
+ }
+
+ Py_XDECREF(self->cx);
+}
+
+PyObject*
+Iterator_next(Iterator* self)
+{
+ PyObject* ret = NULL;
+ jsid propid;
+ jsval propname;
+
+ if(!JS_NextProperty(self->cx->cx, self->iter, &propid))
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to iterate next JS prop.");
+ goto done;
+ }
+
+ if(!JS_IdToValue(self->cx->cx, propid, &propname))
+ {
+ PyErr_SetString(PyExc_RuntimeError, "Failed to convert property id.");
+ goto done;
+ }
+
+ if(propname != JSVAL_VOID)
+ {
+ ret = js2py(self->cx->cx, propname);
+ }
+
+ // We return NULL with no error to signal completion.
+
+done:
+ return ret;
+}
+
+static PyMemberDef Iterator_members[] = {
+ {NULL}
+};
+
+static PyMethodDef Iterator_methods[] = {
+ {NULL}
+};
+
+PyTypeObject _IteratorType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "spidermonkey.Iterator", /*tp_name*/
+ sizeof(Iterator), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Iterator_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "JavaScript Iterator", /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ (iternextfunc)Iterator_next, /*tp_iternext*/
+ Iterator_methods, /*tp_methods*/
+ Iterator_members, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ (initproc)Iterator_init, /*tp_init*/
+ 0, /*tp_alloc*/
+ Iterator_new, /*tp_new*/
+};
View
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2009 Paul J. Davis <paul.joseph.davis@gmail.com>
+ *
+ * This file is part of the python-spidermonkey package released
+ * under the MIT license.
+ *
+ */
+
+#ifndef PYSM_JSITERATOR_H
+#define PYSM_JSITERATOR_H
+
+/*
+ This is a representation of a JavaScript
+ object in Python land.
+*/
+
+typedef struct {
+ PyObject_HEAD
+ Context* cx;
+ JSObject* iter;
+ jsval root;
+} Iterator;
+
+extern PyTypeObject _IteratorType;
+
+PyObject* Iterator_Wrap(Context* cx, JSObject* obj);
+
+#endif
View
@@ -338,6 +338,12 @@ Object_rich_cmp(Object* self, PyObject* other, int op)
return ret;
}
+PyObject*
+Object_iterator(Object* self, PyObject* args, PyObject* kwargs)
+{
+ return Iterator_Wrap(self->cx, self->obj);
+}
+
static PyMemberDef Object_members[] = {
{NULL}
};
@@ -379,7 +385,7 @@ PyTypeObject _ObjectType = {
0, /*tp_clear*/
(richcmpfunc)Object_rich_cmp, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
- 0, /*tp_iter*/
+ (getiterfunc)Object_iterator, /*tp_iter*/
0, /*tp_iternext*/
Object_methods, /*tp_methods*/
Object_members, /*tp_members*/
@@ -18,6 +18,7 @@ PyTypeObject* ContextType = NULL;
PyTypeObject* ObjectType = NULL;
PyTypeObject* ArrayType = NULL;
PyTypeObject* FunctionType = NULL;
+PyTypeObject* IteratorType = NULL;
PyTypeObject* HashCObjType = NULL;
PyObject* JSError = NULL;
@@ -40,6 +41,8 @@ initspidermonkey(void)
_FunctionType.tp_base = &_ObjectType;
if(PyType_Ready(&_FunctionType) < 0) return;
+ if(PyType_Ready(&_IteratorType) < 0) return;
+
if(PyType_Ready(&_HashCObjType) < 0) return;
m = Py_InitModule3("spidermonkey", spidermonkey_methods,
@@ -70,6 +73,10 @@ initspidermonkey(void)
Py_INCREF(FunctionType);
PyModule_AddObject(m, "Function", (PyObject*) FunctionType);
+ IteratorType = &_IteratorType;
+ Py_INCREF(IteratorType);
+ // No module access on purpose.
+
HashCObjType = &_HashCObjType;
Py_INCREF(HashCObjType);
// Don't add access from the module on purpose.
@@ -27,6 +27,7 @@
#include "jsobject.h"
#include "jsarray.h"
#include "jsfunction.h"
+#include "jsiterator.h"
#include "convert.h"
#include "error.h"
@@ -40,6 +41,7 @@ extern PyTypeObject* ClassType;
extern PyTypeObject* ObjectType;
extern PyTypeObject* ArrayType;
extern PyTypeObject* FunctionType;
+extern PyTypeObject* IteratorType;
extern PyTypeObject* HashCObjType;
extern PyObject* JSError;
View
@@ -53,6 +53,12 @@ def lt(a, b):
def gt(a, b):
assert a > b, "%r <= %r" % (a, b)
+def isin(a, b):
+ assert a in b, "%r is not in %r" % (a, b)
+
+def isnotin(a, b):
+ assert a not in b, "%r is in %r" % (a, b)
+
def has(a, b):
assert hasattr(a, b), "%r has no attribute %r" % (a, b)
View
@@ -4,33 +4,29 @@
# under the MIT license.
import t
-@t.rt()
-def test_iter_py(rt):
- pairs = [
- ({"foo": "bar", "baz": "bam"}, ["foo", "baz"]),
- (["a", "b", "c"], [0, 1, 2])
- ]
- def check(a, b):
- cx = rt.new_context()
- cx.add_global("zip", a)
- js = """
- var ret = [];
- for(var v in zip) {ret.push(v);}
- ret;
- """
- t.eq(cx.execute(js), b)
- map(lambda x: check(*x), pairs)
+js_script = """
+var ret = [];
+for(var v in data) {ret.push(v);}
+ret;
+"""
-@t.rt()
-def test_iter_js(rt):
- pairs = [
- ('var f = {"foo": 1, "domino": "daily"}; f;', ["domino", "foo"]),
- ('["foo", 1, "bing"]', [0, 1, 2])
- ]
- def check(a, b):
- cx = rt.new_context()
- ret = cx.execute(a)
- data = [k for k in ret]
- data.sort()
- t.eq(data, b)
- map(lambda x: check(*x), pairs)
+@t.glbl("data", {"foo": "bar", "baz": "bam"})
+def test_iter_py_map(cx, glbl):
+ t.eq(cx.execute(js_script), ["foo", "baz"])
+
+@t.glbl("data", ["a", "b", "c"])
+def test_iter_py_array(cx, glbl):
+ t.eq(cx.execute(js_script), [0, 1, 2])
+
+@t.cx()
+def test_iter_js_object(cx):
+ ret = cx.execute('var f = {"foo": 1, "domino": "daily"}; f;')
+ items = set(["domino", "foo"])
+ for k in ret:
+ t.isin(k, items)
+ items.remove(k)
+
+@t.cx()
+def test_iter_js_array(cx):
+ ret = cx.execute('["foo", 1, "bing", [3, 6]]')
+ t.eq([k for k in ret], ["foo", 1, "bing", [3, 6]])

0 comments on commit 74291c8

Please sign in to comment.