Skip to content

Commit

Permalink
Merge branch 'jsiters'
Browse files Browse the repository at this point in the history
Conflicts:
	tests/test-iterate.py
  • Loading branch information
Paul Davis committed May 10, 2009
2 parents 0d2d6d1 + ddb81d4 commit 74291c8
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 31 deletions.
1 change: 1 addition & 0 deletions THANKS
Expand Up @@ -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
14 changes: 13 additions & 1 deletion spidermonkey/jsarray.c
Expand Up @@ -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.");
Expand Down Expand Up @@ -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}
};
Expand Down Expand Up @@ -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*/
Expand Down
166 changes: 166 additions & 0 deletions spidermonkey/jsiterator.c
@@ -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*/
};
28 changes: 28 additions & 0 deletions spidermonkey/jsiterator.h
@@ -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
8 changes: 7 additions & 1 deletion spidermonkey/jsobject.c
Expand Up @@ -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}
};
Expand Down Expand Up @@ -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*/
Expand Down
7 changes: 7 additions & 0 deletions spidermonkey/spidermonkey.c
Expand Up @@ -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;

Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions spidermonkey/spidermonkey.h
Expand Up @@ -27,6 +27,7 @@
#include "jsobject.h"
#include "jsarray.h"
#include "jsfunction.h"
#include "jsiterator.h"

#include "convert.h"
#include "error.h"
Expand All @@ -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;

Expand Down
6 changes: 6 additions & 0 deletions tests/t.py
Expand Up @@ -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)

Expand Down
54 changes: 25 additions & 29 deletions tests/test-iterate.py
Expand Up @@ -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.