changeset 61:46e6fa692e0c pytave-native

Merge with trunk
author David Grundberg <individ@acc.umu.se>
date Mon, 08 Jun 2009 20:31:38 +0200
parents 04e18a0cf25d (current diff) f379cb14c4d4 (diff)
children 8135e840ef0e a271989ff0eb
files octave_to_python.cc package/pytave.py pytave.cc test/test.py
diffstat 6 files changed, 161 insertions(+), 170 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Jun 08 20:02:16 2009 +0200
+++ b/ChangeLog	Mon Jun 08 20:31:38 2009 +0200
@@ -1,3 +1,31 @@
+2009-06-03  Jaroslav Hajek  <highegg@gmail.com>
+
+	* pytave.cc (delvar): New function.
+	* package/pytave.py (_VariablesDict.__delitem__): New method.
+
+2009-06-03  Jaroslav Hajek  <highegg@gmail.com>
+
+	* pytave.cc (isvar): Fix tests.
+
+2009-06-08  Jaroslav Hajek  <highegg@gmail.com>, David Grundberg  <individ@acc.umu.se>
+
+	* octave_to_python.cc (copy_octarray_to_pyarrobj<PyObject *,
+	Cell>): New template specialization.
+	(createPyArr): Simplify row vectors to 1D arrays.
+	(octvalue_to_pyarrobj): Add string and cell cases.
+	(octcell_to_pyobject): Create a PyObject array if not row vector.
+	(octmap_to_pyobject): Create a simple dict if scalar struct.
+	(octvalue_to_pyobj): Allow converting arbitrary character matrices.
+	* python_to_octave.cc (pydict_to_octmap): Make scalar structs by
+	default, detect matching dimensions.
+	* package/pytave.py (feval): Update doc string.
+	(getvar, setvar, isvar): Remove.
+	(_VariablesDict): New class.
+	(locals, globals): Default instances.
+	(_local_scope): Rename to _LocalScope
+	* test/test.py: New tests. Update old tests.
+	(testsetget, testexception): New functions.
+
 2009-06-03  Jaroslav Hajek  <highegg@gmail.com>
 
 	* python_to_octave.cc (copy_pyarrobj_to_octarray<PyObject *,
--- a/octave_to_python.cc	Mon Jun 08 20:02:16 2009 +0200
+++ b/octave_to_python.cc	Mon Jun 08 20:31:38 2009 +0200
@@ -53,8 +53,7 @@
                                   const unsigned int matindex,
                                   const unsigned int matstride,
                                   const int dimension,
-                                  const unsigned int offset,
-                                  bool = true) {
+                                  const unsigned int offset) {
       unsigned char *ptr = (unsigned char*) pyarr->data;
       if (dimension == pyarr->nd - 1) {
          // Last dimension, base case
@@ -82,14 +81,13 @@
                                   const unsigned int matindex,
                                   const unsigned int matstride,
                                   const int dimension,
-                                  const unsigned int offset,
-                                  bool native) {
+                                  const unsigned int offset) {
       unsigned char *ptr = (unsigned char*) pyarr->data;
       if (dimension == pyarr->nd - 1) {
          // Last dimension, base case
          for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
             object pyobj;
-            octvalue_to_pyobj (pyobj, matrix.elem(matindex + i*matstride), native);
+            octvalue_to_pyobj (pyobj, matrix.elem(matindex + i*matstride));
             Py_INCREF (pyobj.ptr());
             *(PyObject **)&ptr[offset + i*pyarr->strides[dimension]]
                = pyobj.ptr();
@@ -102,30 +100,35 @@
                matindex + i*matstride,
                matstride * pyarr->dimensions[dimension],
                dimension + 1,
-               offset + i*pyarr->strides[dimension],
-               native);
+               offset + i*pyarr->strides[dimension]);
          }
       }
    }
 
    static PyArrayObject *createPyArr(const dim_vector &dims,
                                      int pyarrtype) {
-      int dimensions[dims.length()];
+      int len = dims.length();
+      int dimensions[len];
       for (int i = 0; i < dims.length(); i++) {
          dimensions[i] = dims(i);
       }
+      // Simplify row vectors to 1d arrays.
+      if (len == 2 && dimensions[0] == 1) {
+         dimensions[0] = dimensions[1];
+         len = 1;
+      }
 
       return (PyArrayObject *)PyArray_FromDims(
-         dims.length(), dimensions, pyarrtype);
+         len, dimensions, pyarrtype);
    }
 
    template <class PythonPrimitive, class OctaveBase>
    static PyArrayObject *create_array(const OctaveBase &octarr,
-                                      int pyarraytype, bool native = true) {
+                                      int pyarraytype) {
       PyArrayObject *pyarr = createPyArr(octarr.dims(), pyarraytype);
       try {
          copy_octarray_to_pyarrobj
-            <PythonPrimitive, OctaveBase>(pyarr, octarr, 0, 1, 0, 0, native);
+            <PythonPrimitive, OctaveBase>(pyarr, octarr, 0, 1, 0, 0);
       } catch (value_convert_exception &pe) {
          Py_DECREF(pyarr);
          throw;
@@ -244,13 +247,13 @@
          return create_sint_array<int8NDArray, sizeof(int8_t)>(
             matrix.int8_array_value());
       }
-      if (!native && matrix.is_string()) {
+      if (matrix.is_string()) {
          return create_array<char, charNDArray>(
-            matrix.char_array_value(), PyArray_CHAR, native);
+            matrix.char_array_value(), PyArray_CHAR);
       }
-      if (!native && matrix.is_cell()) {
+      if (matrix.is_cell()) {
          return create_array<PyObject *, Cell>(
-            matrix.cell_value(), PyArray_OBJECT, native);
+            matrix.cell_value(), PyArray_OBJECT);
       }
 
       throw value_convert_exception("Octave matrix type not known, "
@@ -269,24 +272,20 @@
    }
 
    static void octcell_to_pyobject(boost::python::object &py_object,
-                                   const Cell& cell, bool native) {
-      if (native) {
+                                   const Cell& cell) {
+      if (is_1xn_or_0x0 (cell.dims ())) {
          py_object = boost::python::list();
 
-         if(!is_1xn_or_0x0(cell.dims())) {
-            throw value_convert_exception(
-               "Only one-dimensional (row mayor) cell arrays can be converted.");
-         }
-
          for(octave_idx_type i = 0 ; i < cell.length(); i++) {
             boost::python::object py_val;
 
-            octvalue_to_pyobj(py_val, cell.elem(i), native);
+            octvalue_to_pyobj(py_val, cell.elem(i));
 
             ((boost::python::list&)py_object).insert(i, py_val);
          }
-      } else
-         octvalue_to_pyarr(py_object, cell, native);
+      } else {
+         octvalue_to_pyarr (py_object, octave_value (cell));
+      }
    }
 
    static void octmap_to_pyobject(boost::python::object &py_object,
@@ -294,11 +293,18 @@
                                   bool native = true) {
       py_object = boost::python::dict();
       string_vector keys = map.keys();
+      bool scalar = map.dims().all_ones();
 
       for(octave_idx_type i = 0 ; i < keys.length(); i++) {
          boost::python::object py_val;
 
-         octcell_to_pyobject(py_val, map.contents(keys[i]), native);
+         const Cell c = map.contents(keys[i]);
+
+         if (scalar) {
+            octvalue_to_pyobj(py_val, c(0));
+         } else {
+            octcell_to_pyobject(py_val, c);
+         }
 
          py_object[keys[i]] = py_val;
       }
@@ -322,19 +328,18 @@
          else
             throw value_convert_exception(
                "Conversion for this scalar not implemented");
-      } else if (native && octvalue.is_string()) {
-         if (! is_1xn_or_0x0 (octvalue.dims ()))
-            throw value_convert_exception(
-               "Multi-row character matrices can not be converted.");
-         py_object = str(octvalue.string_value());
-      } else if (!native && octvalue.is_string()) {
-         octvalue_to_pyarr(py_object, octvalue, native);
+      } else if (octvalue.is_string()) {
+         if (is_1xn_or_0x0 (octvalue.dims ())) {
+            py_object = object(octvalue.string_value());
+         } else {
+            octvalue_to_pyarr(py_object, octvalue);
+         }
       } else if (octvalue.is_matrix_type()) {
          octvalue_to_pyarr(py_object, octvalue);
       } else if (octvalue.is_map()) {
          octmap_to_pyobject(py_object, octvalue.map_value(), native);
       } else if (octvalue.is_cell()) {
-         octcell_to_pyobject(py_object, octvalue.cell_value(), native); 
+         octcell_to_pyobject(py_object, octvalue.cell_value());
       } else
          throw value_convert_exception(
             "Conversion from Octave value not implemented");
--- a/package/pytave.py	Mon Jun 08 20:02:16 2009 +0200
+++ b/package/pytave.py	Mon Jun 08 20:31:38 2009 +0200
@@ -56,9 +56,9 @@
 	Objects:
 		int (32-bit)        int32
 		float (64-bit)      double
-		str                 string
+		str                 character array
 		dict                struct
-		list                cell
+		list                cell array
 		
 	Numeric Array:
 		UBYTE, SBYTE,       matrix of correct type
@@ -66,11 +66,17 @@
 		UINT, SINT,         -''-
 		LONG,               -''-
 		DOUBLE              -''-
-		CHAR 					  character array
-		OBJECT				  cell array
+		CHAR                character array
+		OBJECT              cell array
 
 	All other objects causes a pytave.ObjectConvertError to be
 	raised. This exception inherits TypeError.
+
+	When dicts are converted, all keys must be strings and
+	constitute valid Octave identifiers. By default, scalar
+	structures are created. However, when all values evaluate
+	to cell arrays with matching dimensions, an Octave struct
+	array is created.
 	
 	Octave to Python
 	================
@@ -78,25 +84,18 @@
 	Scalar values to objects:
 		bool                bool
 		real scalar         float (64-bit)
-		any string          str* (if native = True)
 		struct              dict
-		cell                list* (if native = True)
 
-		* Cell arrays must be one-dimensional (row vector) and
-                  character matrices must only have one row.  Any
-                  other form will raise a ValueConvertError.
-						This is required unless native = False.
-		
 	Matrix values to Numeric arrays:
-	   double				  DOUBLE
-		single				  FLOAT
-		logical				  DOUBLE
+	   	double              DOUBLE
+		single              FLOAT
+		logical             DOUBLE
 		int64               LONG
 		int32, uint32       INT, UINT
 		int16, uint16       SHORT, USHORT
 		int8, unint8        SBYTE, UBYTE
-		char					  CHAR (if native = False)
-		cell					  OBJECT (if native = False)
+		char                CHAR (if native = False)
+		cell                OBJECT (if native = False)
 
 	All other values causes a pytave.ValueConvertError to be
 	raised. This exception inherits TypeError.
@@ -109,8 +108,7 @@
 
 	"""
 
-	return _pytave.feval(nargout, funcname, arguments,
-			  kwargs.get("native", True))
+	return _pytave.feval(nargout, funcname, arguments)
 
 def eval(nargout, code, **kwargs):
 
@@ -145,8 +143,7 @@
 
 	"""
 
-	return _pytave.eval(nargout,code,kwargs.get("silent",True),
-		 kwargs.get("native",True))
+	return _pytave.eval(nargout,code,kwargs.get("silent",True))
 
 def addpath(*arguments):
 	"""See Octave documentation"""
@@ -160,13 +157,8 @@
 	"""See Octave documentation"""
 	return _pytave.feval(1, "path", paths)[0]
 
-#xxx
-_pytave.setvar("foo", "value", True)
-print _pytave.isvar("foo", True)
-print _pytave.getvar("foo", True, True)
-#xxx
 class _VariablesDict(UserDict.DictMixin):
-	def __init__(self, global_variables, native):
+	def __init__(self, global_variables, native=False):
 		self.global_variables = global_variables
 		self.native = native
 
@@ -174,7 +166,7 @@
 		if not isinstance(name, basestring):
 			raise TypeError('Expected a string, not a ' + repr(type(name)))
 		try:
-			return _pytave.getvar(name, self.global_variables, self.native)
+			return _pytave.getvar(name, self.global_variables)
 		except VarNameError:
 			raise KeyError('No Octave variable named ' + name)
 
@@ -191,16 +183,17 @@
 	def __delitem__(self, name):
 		if not isinstance(name, basestring):
 			raise TypeError('Expected a string, not a ' + repr(type(name)))
-		try:
-			_pytave.clearvar(name, self.global_variables)
-		except VarNameError:
+		# Octave does not gripe when clearing non-existent
+		# variables. To be consistent with Python dict
+		# behavior, we shall do so.
+		if self.__contains__(name):
+			_pytave.delvar(name, self.global_variables)
+		else:
 			raise KeyError('No Octave variable named ' + name)
 
 
-locals = _VariablesDict(global_variables=False, native=False)
-globals = _VariablesDict(global_variables=True, native=False)
-native_locals = _VariablesDict(global_variables=False, native=True)
-native_globals = _VariablesDict(global_variables=True, native=True)
+locals = _VariablesDict(global_variables=False)
+globals = _VariablesDict(global_variables=True)
 
 def push_scope():
 	 """Creates a new anonymous local variable scope on the Octave call
--- a/pytave.cc	Mon Jun 08 20:02:16 2009 +0200
+++ b/pytave.cc	Mon Jun 08 20:31:38 2009 +0200
@@ -129,8 +129,7 @@
      
    boost::python::tuple func_eval(const int nargout,
                                   const string &funcname,
-                                  const boost::python::tuple &arguments,
-                                  bool native) {
+                                  const boost::python::tuple &arguments) {
 
       octave_value_list octave_args, retval;
 
@@ -161,7 +160,7 @@
 
       if (nargout >= 0) {
          boost::python::tuple pytuple;
-         octlist_to_pytuple(pytuple, retval, native);
+         octlist_to_pytuple(pytuple, retval);
          return pytuple;
       } else {
          // Return () if nargout < 0.
@@ -171,8 +170,7 @@
 
    boost::python::tuple str_eval(int nargout,
                                  const string &code,
-                                 bool silent,
-                                 bool native) {
+                                 bool silent) {
 
       octave_value_list retval;
       int parse_status;
@@ -207,7 +205,7 @@
 
       if (nargout >= 0) {
          boost::python::tuple pytuple;
-         octlist_to_pytuple(pytuple, retval, native);
+         octlist_to_pytuple(pytuple, retval);
          return pytuple;
       } else {
          // Return () if nargout < 0.
@@ -216,7 +214,7 @@
    }
 
    boost::python::object getvar(const string& name,
-                                bool global, bool native) {
+                                bool global) {
       octave_value val;
 
       if (global)
@@ -229,7 +227,7 @@
       }
 
       boost::python::object pyobject;
-      octvalue_to_pyobj(pyobject, val, native);
+      octvalue_to_pyobj(pyobject, val);
 
       return pyobject;
    }
@@ -265,21 +263,27 @@
    bool clearvar(const string& name, bool global) {
       bool retval;
 
-      if (! isvar (name, global))
-         {
-            throw variable_name_exception (name + " not defined in " + ((global) ? "global" : "local") + " table");
-         }
-
-//       if (global)
-//          symbol_table::clear_global (name);
-//       else
-//          symbol_table::clear_variable (name);
-      symbol_table::clear_global (name);
-      symbol_table::clear_variable (name);
+      if (global)
+         retval = symbol_table::global_varval (name).is_defined ();
+      else
+         retval = symbol_table::is_variable (name);
 
       return retval;
    }
 
+   void delvar(const string& name, bool global) {
+
+      if (global) {
+
+         // FIXME: workaround a bug in Octave 3.2.0.
+         if (! symbol_table::is_global (name))
+            symbol_table::insert (name).mark_global ();
+
+         symbol_table::clear_global (name);
+      } else
+         symbol_table::clear_variable (name);
+   }
+
    int push_scope() {
       symbol_table::scope_id local_scope = symbol_table::alloc_scope();
       symbol_table::set_scope(local_scope);
@@ -307,7 +311,7 @@
    def("getvar", pytave::getvar);
    def("setvar", pytave::setvar);
    def("isvar", pytave::isvar);
-   def("clearvar", pytave::clearvar);
+   def("delvar", pytave::delvar);
    def("push_scope", pytave::push_scope);
    def("pop_scope", pytave::pop_scope);
    def("get_exceptions", pytave::get_exceptions);
--- a/python_to_octave.cc	Mon Jun 08 20:02:16 2009 +0200
+++ b/python_to_octave.cc	Mon Jun 08 20:31:38 2009 +0200
@@ -310,11 +310,14 @@
 
       dim_vector dims = dim_vector(1, 1);
 
-      bool has_dimensions = false;
+      bool dims_match = true;
 
       Array<octave_value> vals (length);
       Array<std::string> keys (length);
 
+      // Extract all keys and convert values. Remember whether dimensions
+      // match.
+      
       for(octave_idx_type i = 0; i < length; i++) {
 
          std::string& key = keys(i);
@@ -343,23 +346,15 @@
 
          pyobj_to_octvalue(val, tuple[1]);
 
-         if(val.is_cell()) {
-            const Cell c(val.cell_value());
-            if (error_state)
-               throw object_convert_exception("Octave error");
-
-            // We do not bother measuring 1x1 values, since they are replicated
-            // to fill up the necessary dimensions.
-            if(!(c.dims().length() == 2 && c.dims()(0) == 1 && c.dims()(1) == 1)) {
-
-               if(!has_dimensions) {
-                  dims = c.dims();
-                  has_dimensions = true;
-               } else if(c.dims() != dims) {
-                  throw object_convert_exception(
-                     "Dimensions of the struct fields do not match");
-               }
+         if(dims_match && val.is_cell()) {
+            dim_vector dv = val.dims();
+            if(i == 0) {
+               dims = dv;
+            } else {
+               dims_match = dims == dv;
             }
+         } else {
+            dims_match = false;
          }
       }
 
@@ -370,23 +365,10 @@
          std::string& key = keys(i);
          octave_value val = vals(i);
 
-         if(!val.is_cell()) {
-            map.assign(key, Cell(dims, val));
+         if(dims_match) {
+            map.assign(key, val.cell_value ());
          } else {
-            const Cell c(val.cell_value());
-
-            if (error_state)
-               throw object_convert_exception("Octave error");
-
-            if(c.dims().length() == 2 && c.dims()(0) == 1 && c.dims()(1) == 1) {
-               map.assign(key, Cell(dims, c(0)));
-            }
-            else {
-               map.assign(key, c);
-            }
-         }
-         if (error_state) {
-            throw object_convert_exception("Octave error");
+            map.assign(key, val);
          }
       }
       oct_value = map;
--- a/test/test.py	Mon Jun 08 20:02:16 2009 +0200
+++ b/test/test.py	Mon Jun 08 20:31:38 2009 +0200
@@ -12,26 +12,26 @@
 arr1_0 = Numeric.zeros((1,0));
 number = Numeric.array([1.32], Numeric.Float32)
 arr1fT = Numeric.array([[1.32], [2], [3], [4]], Numeric.Float32)
-arr1fT2 = Numeric.array([[1.32, 2, 3, 4]], Numeric.Float32)
-arr1f = Numeric.array([[1.32, 2, 3, 4]], Numeric.Float32)
-arr1b = Numeric.array([[8, 2, 3, 256]], Numeric.Int8)
-arr1i = Numeric.array([[17, 2, 3, 4]], Numeric.Int)
-arr1i32 = Numeric.array([[32, 2, 3, 4]], Numeric.Int32)
-arr1a = Numeric.array([[1, 2, 3, 4]])
+arr1fT2 = Numeric.array([1.32, 2, 3, 4], Numeric.Float32)
+arr1f = Numeric.array([1.32, 2, 3, 4], Numeric.Float32)
+arr1b = Numeric.array([8, 2, 3, 256], Numeric.Int8)
+arr1i = Numeric.array([17, 2, 3, 4], Numeric.Int)
+arr1i32 = Numeric.array([32, 2, 3, 4], Numeric.Int32)
+arr1a = Numeric.array([1, 2, 3, 4])
 arr2f = Numeric.array([[1.32, 2, 3, 4],[5,6,7,8]], Numeric.Float32)
 arr2d = Numeric.array([[1.17, 2, 3, 4],[5,6,7,8]], Numeric.Float)
 arr3f = Numeric.array([[[1.32, 2, 3, 4],[5,6,7,8]],[[9, 10, 11, 12],[13,14,15,16]]], Numeric.Float32)
-arr1c = Numeric.array([[1+2j, 3+4j, 5+6j, 7+0.5j]], Numeric.Complex)
-arr1fc = Numeric.array([[1+2j, 3+4j, 5+6j, 7+0.5j]], Numeric.Complex32)
-arr1o = Numeric.array([["abc",1.0,2+3j]],Numeric.PyObject)
-arr2o = Numeric.array([["abc",1.0,2+3j],[4.0,arr1i,"def"]],Numeric.PyObject)
-arr1ch = Numeric.array(["abc"],Numeric.Character)
+arr1c = Numeric.array([1+2j, 3+4j, 5+6j, 7+0.5j], Numeric.Complex)
+arr1fc = Numeric.array([1+2j, 3+4j, 5+6j, 7+0.5j], Numeric.Complex32)
+arr1ch = Numeric.array("abc",Numeric.Character)
 arr2ch = Numeric.array(["abc","def"],Numeric.Character)
+arr1o = Numeric.array([1.0,"abc",2+3j],Numeric.PyObject)
+arr2o = Numeric.array([[1.0,"abc",2+3j],[4.0,arr1i,"def"]],Numeric.PyObject)
 
-alimit_int32 = Numeric.array([[-2147483648, 2147483647]], Numeric.Int32);
-alimit_int16 = Numeric.array([[-32768, 32767, -32769, 32768]], Numeric.Int16);
-alimit_int8 = Numeric.array([[-128, 127, -129, 128]], Numeric.Int8);
-alimit_uint8 = Numeric.array([[0, 255, -1, 256]], Numeric.UnsignedInt8);
+alimit_int32 = Numeric.array([-2147483648, 2147483647], Numeric.Int32);
+alimit_int16 = Numeric.array([-32768, 32767, -32769, 32768], Numeric.Int16);
+alimit_int8 = Numeric.array([-128, 127, -129, 128], Numeric.Int8);
+alimit_uint8 = Numeric.array([0, 255, -1, 256], Numeric.UnsignedInt8);
 
 
 # This eval call is not to be seen as a encouragement to use Pytave
@@ -72,7 +72,7 @@
 			fail("as %s != %s" % (value, nvalue))
 		if value.shape != nvalue.shape:
 			fail("Size check failed for: %s. Expected shape %s, got %s  with shape %s" \
-			%(value, value.shape, nvalue.shape, nvalue))
+			%(value, value.shape, nvalue, nvalue.shape))
 		if class1 != class2:
 			fail( "Type check failed for: %s. Expected %s. Got %s."
 			%(value, class1, class2))
@@ -163,21 +163,14 @@
     except Exception, e:
 	fail("testlocalscope: %s" % (x,), e)
 
-
-testequal('a')
-
-
 testmatrix(alimit_int32)
 testmatrix(alimit_int16)
 testmatrix(alimit_int8)
 
 # Strings
 # Multi-row character matrix cannot be returned
-testvalueerror("eval", "['foo'; 'bar']")
-testequal('a')
 
 testequal("mystring")
-testequal('mystring')
 testequal("mystringåäöÅÄÖ")
 
 testequal(1)
@@ -192,12 +185,12 @@
 testmatrix(arr1fT2)
 testmatrix(arr1i)
 testmatrix(arr1b)
-testmatrix(arr1c)
 testmatrix(arr1fc)
 
 # 2d arrays
 testmatrix(arr2f)
 testmatrix(arr2d)
+testmatrix(arr2ch)
 
 # 3d arrays
 testmatrix(arr3f)
@@ -207,7 +200,6 @@
 	print "FAIL: Zero test", 
 
 testmatrix(arr0_0)
-testmatrix(arr1_0)
 testmatrix(arr0_1)
 
 # Lists
@@ -220,38 +212,24 @@
 testvalueok("cell", 1, 3);
 testvalueok("cell", 1, 0)
 testvalueok("cell", 0, 0)
-
-# Return cells with incompatible dimensions
-testvalueerror("cell", 3, 1)
-testvalueerror("cell", 0, 1)
+testvalueok("cell", 3, 1)
+testvalueok("cell", 0, 1)
 
 # Dictionaries
 
 # Simple dictionary tests
-testequal({"foo": [1], "bar": [2]})
+testequal({"foo": 1, "bar": 2})
 testequal({"x": [1, 3], "y": [2, 4]})
 testequal({"x": [1, "baz"], "y": [2, "foobar"]})
 testequal({"x": [arr1f], "y": [arr1i]})
 testequal({})
+testequal({"foo": arr1f,    "bar": arr2f})
+testequal({"foo": 1,        "bar": [2]})
+testexpect({"foo": [[1,2]],        "bar": [[3,2]]},
+	   {"foo": [1,2],        "bar": [3,2]})
 
 # Try some odd dictionaries
 # The implicit conversion makes Pytave return cell-wrapped results.
-testexpect({"foo": number,   "bar": 2},
-	   {"foo": [number], "bar": [2]})
-testexpect({"foo": arr1f,    "bar": arr2f},
-	   {"foo": [arr1f],  "bar": [arr2f]})
-testexpect({"foo": 1,        "bar": 2},
-	   {"foo": [1],      "bar": [2]})
-testexpect({"foo": 1,        "bar": [2]},
-	   {"foo": [1],      "bar": [2]})
-testexpect({"foo": 1,        "bar": [2, 3]},
-	   {"foo": [1, 1],   "bar": [2, 3]})
-testexpect({"foo": [1],      "bar": [2, 4]},
-	   {"foo": [1, 1],   "bar": [2, 4]})
-testexpect({"bar": 1,        "foo": [2, 3]},
-	   {"bar": [1, 1],   "foo": [2, 3]})
-testexpect({"bar": [1],      "foo": [2, 4]},
-	   {"bar": [1, 1],   "foo": [2, 4]})
 
 # Try some invalid keys
 testobjecterror({"this is not an Octave identifier": 1})
@@ -263,8 +241,8 @@
 testobjecterror(())
 
 result, = pytave.feval(1, "eval", "[1, 1, 1]")
-if result.shape != (1, 3):
-	print "FAIL: expected 1x3 matrix"
+if result.shape != (3,):
+	print "FAIL: expected length-3 vector"
 
 result, = pytave.feval(1, "eval", "[1; 2; 3]");
 if result.shape != (3, 1):
@@ -273,7 +251,8 @@
 testparseerror(1, "endfunction")
 testevalexpect(1, "2 + 2", (4,))
 testevalexpect(1, "{2}", ([2],))
-testevalexpect(2, "struct('foo', 2)", ({'foo': [2]},))
+testevalexpect(1, "struct('foo', 'bar')", ({'foo': Numeric.array(['bar'], Numeric.PyObject)},))
+testevalexpect(1, "struct('foo', {'bar', 'baz'})", ({'foo': Numeric.array(['bar', 'baz'], Numeric.PyObject)},))
 
 testsetget(pytave.locals, "xxx", [1,2,3])
 testsetget(pytave.globals, "xxx", [1,2,3])