changeset 82:f6e8abc9a8cf task

merge
author David Grundberg <c04dgg@cs.umu.se>
date Mon, 24 Aug 2009 11:02:29 +0200
parents 798efee55cd0 (current diff) 6517417e75b2 (diff)
children 8145ecfecfb9
files
diffstat 14 files changed, 1112 insertions(+), 296 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat May 09 15:50:16 2009 +0200
+++ b/ChangeLog	Mon Aug 24 11:02:29 2009 +0200
@@ -1,3 +1,180 @@
+2009-07-03  David Grundberg  <individ@acc.umu.se>
+
+	* pytave.cc: Sorted includes.
+	(atexit): New function. Call do_octave_atexit before Python
+	unloads shared libraries.
+	* package/pytave.py (_atexit): New function.
+
+2009-07-01  David Grundberg  <individ@acc.umu.se>
+
+	* pytave.cc [HAVE_USELOCALE] (init, func_eval, str_eval): Run
+	Octave interpreter in C locale.
+	* configure.ac: Define HAVE_USELOCALE. Substitute @PYTHON@. Added
+	checks for integer typedefs.  Make setup.py executable.  Friendly
+	message on configuration completion.
+	* setup.py.in: Let setup.py be interpreted by the same version of
+	Python we configured for.
+
+2009-06-19  Jaroslav Hajek  <highegg@gmail.com>
+
+	* configure.ac: Support --enable-numpy
+	* setup.py.in: Dynamically determine NumPy include path.
+	* pytave.cc (get_module_name): New function.
+	* octave_to_python.cc (octvalue_to_pyarrobj): 
+	Support bool arrays with NumPy.
+	* python_to_octave.cc (pyarr_to_octvalue, 
+	copy_pyarrobj_to_octarray_boot): Likewise.
+	* package/pytave.py: Dynamically import Numeric,
+	forward to numpy.oldnumeric if run with NumPy.
+	* test/test.py: Update some tests.
+
+2009-06-17  Jaroslav Hajek  <highegg@gmail.com>
+
+	* package/pytave.py (stripdict): New function.
+	(narrowlist): New function.
+	(simplify): New function.
+
+2009-06-16  Jaroslav Hajek  <highegg@gmail.com>
+
+	* octave_to_python.cc 
+	(createPyArr): Don't simplify row vectors to 1D arrays.
+	(octvalue_to_pyarrobj): Simplify, don't special-case scalars.
+	(octcell_to_pyobject): Remove.
+	(octmap_to_pyobject): Simplify, return uniform results.
+	(is_1xn_or_0x0): Remove.
+	* python_to_octave.cc
+	(pydict_to_octmap): Revert to the former behaviour aka Octave's
+	`struct'.
+	* package/pytave.py (canonicalize): Remove.
+	* test/test.py: Update tests.
+
+2009-06-09  Jaroslav Hajek  <highegg@gmail.com>
+
+	* package/pytave.py (canonicalize): New function.
+	(feval): Update doc string.
+
+2009-06-08  David Grundberg  <individ@acc.umu.se>
+
+	* pytave.cc (func_eval, str_eval): Reread changed files.
+
+2009-06-08  David Grundberg  <individ@acc.umu.se>
+
+	* test/test.py: New tests for pytave.globals and pytave.locals.
+	(testsetget, testexception): Call fail() instead of print.
+
+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 *,
+	Cell>): New template specialization.
+	(matching_type<PyObject *, octave_value>): Ditto.
+	(copy_pyarrobj_to_octarray_boot): Include PyArray_CHAR
+	and PyArray_Object cases.
+	(pyarr_to_octvalue): Likewise.
+	* test/test.py: Add tests for the new conversions.
+
+2009-06-03  David Grundberg  <individ@acc.umu.se>
+
+	* octave_to_python.cc (create_uint_array, create_sint_array):
+	Prefer int to other datatypes of identical size.
+	(is_1xn_or_0x0): Whitespace.
+	* python_to_octave.cc (copy_pyarrobj_to_octarray_dispatch): Prefer
+	int to other datatypes of identical size.
+
+2009-05-28  Jaroslav Hajek  <highegg@gmail.com>
+
+	* configure.ac: Remove --enable-float-matrices option.
+	* octave_to_python.cc: Remove uses of PYTAVE_USE_OCTAVE_FLOAT.
+	* python_to_octave.cc: Ditto.
+
+2009-05-26  Jaroslav Hajek  <highegg@gmail.com>
+
+	* octave_to_python.cc (octvalue_to_pyarrobj): Support Complex
+	and FloatComplex values.
+	(octvalue_to_pyobj): Support complex scalars.
+	* python_to_octave.cc (copy_pyarrobj_to_octarray_dispatch):
+	New template function.
+	(matching_type): New helper traits class.
+	(copy_pyarrobj_to_octarray_dispatch): Support complex types.
+	(pyarrobj_to_octvalue): Support complex scalars.
+	* test/test.py: Add tests for complex values.
+
+2009-05-26  Jaroslav Hajek  <highegg@gmail.com>
+
+	* octave_to_python.cc: New #include (boost/type_traits).
+	(copy_octarray_to_pyarrobj): Don't use .value(); don't	specialize.
+	(create_array (..., boost::true_type)): New overload (forward).
+	(create_array (..., boost::false_type)): New overload (dummy).
+	(create_uint_array): Pick proper overload.
+	(create_sint_array): Pick proper overload.
+
+2009-05-25  Jaroslav Hajek  <highegg@gmail.com>
+
+	* python_to_octave.cc (pydict_to_octmap): Save key and val in an
+	array, to avoid doing duplicate conversions.
+
+2009-05-25  Jaroslav Hajek  <highegg@gmail.com>
+
+	* pytave.cc (init): Add parameter; only display Octave banner if
+	requested.
+	* package/pytave.cc: Determine whether the interpreter is
+	interactive.
+
+2009-05-25  Jaroslav Hajek  <highegg@gmail.com>
+
+	* octave_to_python.cc (is_1xn_or_0x0): New inline func.
+	(octcell_to_pyobject): Use it.
+	(octvalue_to_pyobj): Use it.
+	* test/test.py: Update & fix tests.
+
+2009-05-13  Jaroslav Hajek  <highegg@gmail.com>
+
+	* exceptions.h (variable_name_exception): New exception class.
+	* exceptions.cc: Initialize it.
+	* octave_to_python.h (octvalue_to_pyobj): Declare prototype.
+	* pytave.cc (init): Init variable_name_exception.
+	(getvar): New function.
+	(setvar): New function.
+	(isvar): New function.
+	(push_scope): New function.
+	(pop_scope): New function.
+	* package/pytave.py (__init__): Init VarNameError.
+	(getvar): New function.
+	(setvar): New function.
+	(isvar): New function.
+	(push_scope): New function.
+	(pop_scope): New function.
+	(_local_scope): New decorator helper class.
+	(local_scope): New decorator.
+	* test/test.py: Include new tests.
+
 2009-05-09  David Grundberg  <individ@acc.umu.se>
 
 	* ax_octave_float.m4: New file.
--- a/NEWS	Sat May 09 15:50:16 2009 +0200
+++ b/NEWS	Mon Aug 24 11:02:29 2009 +0200
@@ -1,5 +1,44 @@
 Version 0.1.1-bzr 
 
+2009-05-25
+
+* Added functionality for explicit manipulation of variables.
+  getvar, setvar, isvar can be used to get, set and query variables
+  in the current Octave scope.
+  Example:
+    pytave.setvar("x", 1)
+    pytave.eval(0,"x += 1")
+    x = pytave.getvar("x")
+
+* Added functionality to push/pop anonymous scopes on the Octave call 
+  stack. push_scope and pop_scope are provided to create an anonymous scope
+  and push it on Octave's call stack, to prevent cluttering other variables if
+  nested calls to pytave are in effect.
+
+  Example:
+    pytave.push_scope()
+    pytave.setvar("x", something)
+    pytave.eval(0, "... do something with x)
+    pytave.pop_scope() # wipes out x and restores its previous value, if any
+
+  Of course, for proper safety, try/finally block should be used to ensure the
+  cleanup. For convenience, a local_scope decorator is provided that encloses a
+  whole function in a push_scope/try/finally/pop_scope sequence:
+
+    @pytave.local_scope
+    def my_oct_add(x,y):
+        pytave.setvar("x",x)
+        pytave.setvar("y",y)
+        result, = pytave.eval(1, "x + y")
+        return result
+
+  this function, when called, will not affect the top-level values of x and y, if
+  any.
+
+* The Octave welcome banner is now only displayed if Python is run interactively.
+
+* {}, "" and '' are now accepted as return values and converted to an empty list/string.
+
 2009-05-07
 
 * Added an eval function. A string of Octave code can be executed
--- a/arrayobjectdefs.h	Sat May 09 15:50:16 2009 +0200
+++ b/arrayobjectdefs.h	Mon Aug 24 11:02:29 2009 +0200
@@ -29,7 +29,16 @@
 #endif
 #define PY_ARRAY_UNIQUE_SYMBOL pytave_array_symbol
 #include <Python.h>
+#ifdef HAVE_NUMPY
+#include <numpy/oldnumeric.h>
+#include <numpy/old_defines.h>
+// Avoid deprecation warnings from NumPy
+#undef PyArray_FromDims 
+#define PyArray_FromDims PyArray_SimpleNew
+#else
 #include <Numeric/arrayobject.h>
+typedef int npy_intp;
+#endif
 
 /* Emacs
  * Local Variables:
--- a/configure.ac	Sat May 09 15:50:16 2009 +0200
+++ b/configure.ac	Mon Aug 24 11:02:29 2009 +0200
@@ -17,36 +17,21 @@
 
 AC_PRESERVE_HELP_ORDER
 
-AC_ARG_ENABLE(float-matrices,
-	[AS_HELP_STRING([--enable-float-matrices],
-		[use Octave float matrices (experimental)
-		@<:@default=no@:>@])],
-	[pytave_enable_float="$enableval"],
-	[pytave_enable_float=no]) dnl TODO: Change to check someday
+AC_ARG_ENABLE(numpy,
+	      [AS_HELP_STRING([--enable-numpy],
+			      [use NumPy module (experimental)
+			      @<:@default=no@:>@])],
+			      [pytave_enable_numpy="$enableval"],
+			      [pytave_enable_numpy=no]) dnl TODO: Check?
+ 
+if test "$pytave_enable_numpy" == "yes" ; then
+	AC_DEFINE([HAVE_NUMPY], 1, [Define if using NumPy])
+fi
 
 pytave_libs_ok=
 
 AX_OCTAVE([], [], [pytave_libs_ok=no])
 
-AS_IF([test "x$pytave_enable_float" = "xcheck" dnl
-            -o "x$pytave_enable_float" = "xyes"], [
-	AX_OCTAVE_FLOAT([
-		AC_DEFINE([PYTAVE_USE_OCTAVE_FLOATS], [],
-			[Define to enable conversion of Octave float
-			matrices. If not defined, float matrices are
-			expanded to double matrices])
-	], [
-		AS_IF([test "x$pytave_enable_float" = "xyes"], [
-			AC_WARN([[
-========================================================================
-Float matrices enabled, but could not compile test program against
-Octave.
-========================================================================]])
-			pytave_libs_ok=no
-		])
-	])
-])
-
 # Pick a Python library to use
 AX_PYTHON_DEVEL([], [], [pytave_libs_ok=no])
 
@@ -89,12 +74,23 @@
 # Checks for libraries.
 
 # Checks for header files.
+AC_CHECK_HEADERS([locale.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_HEADER_STDBOOL
 AC_C_CONST
+AC_C_INLINE
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_INT8_T
+AC_TYPE_SIZE_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
 
 # Checks for library functions.
+AC_CHECK_FUNCS([uselocale], [pytave_have_uselocale=yes], [pytave_have_uselocale=no])
 
 # This needs a more usable, less unusual solution.
 AS_IF(test "x${prefix}" == "xNONE",
@@ -111,11 +107,32 @@
 PYTAVE_OCTAVE_RPATH="$OCTAVE_LIBRARYDIR"
 AC_SUBST(PYTAVE_OCTAVE_RPATH)
 AC_SUBST(PYTAVE_MODULE_INSTALL_PATH)
+AC_SUBST(pytave_enable_numpy)
 
 # Substitutes for the Jamfile. XXX: Replace lib*.so with OS independent name.
 AC_SUBST(JAM_LIBOCTAVE, $OCTAVE_LIBRARYDIR/liboctave.so)
 AC_SUBST(JAM_LIBCRUFT, $OCTAVE_LIBRARYDIR/libcruft.so)
 AC_SUBST(JAM_LIBOCTINTERP, $OCTAVE_LIBRARYDIR/liboctinterp.so)
 
+# setup.py
+AC_SUBST(PYTHON)
+
 # Substitute in these files, copy project-root.jam to VPATH too
 AC_OUTPUT([Makefile Jamfile setup.py project-root.jam])
+
+chmod u+x "setup.py"
+
+AC_MSG_NOTICE([
+========================================================================
+Pytave now configured with the following setup:
+
+Dependencies
+  Octave ............. $OCTAVE_INCLUDEDIR
+  Python ............. $PYTHON_CPPFLAGS
+    executable ....... $PYTHON
+
+Features
+  NumPy .............. $pytave_enable_numpy
+  uselocale .......... $pytave_have_uselocale
+
+========================================================================])
--- a/exceptions.cc	Sat May 09 15:50:16 2009 +0200
+++ b/exceptions.cc	Mon Aug 24 11:02:29 2009 +0200
@@ -26,6 +26,7 @@
 	PyObject *value_convert_exception::excclass = NULL;
 	PyObject *object_convert_exception::excclass = NULL;
 	PyObject *octave_parse_exception::excclass = NULL;
+	PyObject *variable_name_exception::excclass = NULL;
 
 }
 
--- a/exceptions.h	Sat May 09 15:50:16 2009 +0200
+++ b/exceptions.h	Mon Aug 24 11:02:29 2009 +0200
@@ -113,6 +113,26 @@
          std::string error;
    };
 
+   class variable_name_exception {
+      public:
+         static bool init() {
+            excclass = PyErr_NewException(
+               const_cast<char*>("pytave.VarNameError"),
+               PyExc_RuntimeError, NULL);
+            return excclass != NULL;
+         };
+         static void translate_exception(variable_name_exception const &py_ex) {
+            PyErr_SetString(excclass, py_ex.error.c_str());
+         }
+         static PyObject *excclass;
+
+         variable_name_exception(std::string err) { error = err; };
+
+      private:
+         std::string error;
+
+   };
+
 }
 
 #endif /* PYTAVE_EXCEPTIONS_H */
--- a/octave_to_python.cc	Sat May 09 15:50:16 2009 +0200
+++ b/octave_to_python.cc	Mon Aug 24 11:02:29 2009 +0200
@@ -1,5 +1,6 @@
 /*
  *  Copyright 2008 David Grundberg, Håkan Fors Nilsson
+ *  Copyright 2009 VZLU Prague
  *
  *  This file is part of Pytave.
  *
@@ -20,6 +21,7 @@
 #include "arrayobjectdefs.h"
 #include <boost/python.hpp>
 #include <boost/python/numeric.hpp>
+#include <boost/type_traits/integral_constant.hpp>
 #undef HAVE_STAT /* both boost::python and octave define HAVE_STAT... */
 #include <octave/oct.h>
 #include <octave/Matrix.h>
@@ -29,6 +31,7 @@
 #include <iostream>
 #include "pytavedefs.h"
 #include "exceptions.h"
+#include "octave_to_python.h"
 
 /* From docs:
  *  Note that the names of the element type constants refer to the C data
@@ -43,9 +46,6 @@
 
 namespace pytave {
 
-   void octvalue_to_pyobj(boost::python::object &py_object,
-                          const octave_value& octvalue);
-
    template <class PythonPrimitive, class OctaveBase>
    static void copy_octarray_to_pyarrobj(
                                   PyArrayObject *pyarr,
@@ -59,7 +59,7 @@
          // Last dimension, base case
          for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
             *(PythonPrimitive *)&ptr[offset + i*pyarr->strides[dimension]]
-               = matrix.elem(matindex + i*matstride).value();
+               = matrix.elem(matindex + i*matstride);
          }
       } else {
          for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
@@ -75,9 +75,9 @@
    }
 
    template <>
-   void copy_octarray_to_pyarrobj<double, NDArray>(
+   void copy_octarray_to_pyarrobj<PyObject *, Cell>(
                                   PyArrayObject *pyarr,
-                                  const NDArray &matrix,
+                                  const Cell &matrix,
                                   const unsigned int matindex,
                                   const unsigned int matstride,
                                   const int dimension,
@@ -86,12 +86,15 @@
       if (dimension == pyarr->nd - 1) {
          // Last dimension, base case
          for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
-            *(double *)&ptr[offset + i*pyarr->strides[dimension]]
-               = matrix.elem(matindex + i*matstride);
+            object pyobj;
+            octvalue_to_pyobj (pyobj, matrix.elem(matindex + i*matstride));
+            Py_INCREF (pyobj.ptr());
+            *(PyObject **)&ptr[offset + i*pyarr->strides[dimension]]
+               = pyobj.ptr();
          }
       } else {
          for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
-            copy_octarray_to_pyarrobj<double, NDArray>(
+            copy_octarray_to_pyarrobj<PyObject *, Cell>(
                pyarr,
                matrix,
                matindex + i*matstride,
@@ -102,45 +105,16 @@
       }
    }
 
-#ifdef PYTAVE_USE_OCTAVE_FLOATS
-   template <>
-   void copy_octarray_to_pyarrobj<float, FloatNDArray>(
-                                  PyArrayObject *pyarr,
-                                  const FloatNDArray &matrix,
-                                  const unsigned int matindex,
-                                  const unsigned int matstride,
-                                  const int dimension,
-                                  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++) {
-            *(float *)&ptr[offset + i*pyarr->strides[dimension]]
-               = matrix.elem(matindex + i*matstride);
-         }
-      } else {
-         for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
-            copy_octarray_to_pyarrobj<float, FloatNDArray>(
-               pyarr,
-               matrix,
-               matindex + i*matstride,
-               matstride * pyarr->dimensions[dimension],
-               dimension + 1,
-               offset + i*pyarr->strides[dimension]);
-         }
-      }
-   }
-#endif /* PYTAVE_USE_OCTAVE_FLOATS */
-
    static PyArrayObject *createPyArr(const dim_vector &dims,
                                      int pyarrtype) {
-      int dimensions[dims.length()];
+      int len = dims.length();
+      npy_intp dimensions[len];
       for (int i = 0; i < dims.length(); i++) {
          dimensions[i] = dims(i);
       }
 
       return (PyArrayObject *)PyArray_FromDims(
-         dims.length(), dimensions, pyarrtype);
+         len, dimensions, pyarrtype);
    }
 
    template <class PythonPrimitive, class OctaveBase>
@@ -157,14 +131,31 @@
       return pyarr;
    }
 
+   template <class PythonPrimitive, class OctaveBase>
+   static PyArrayObject *create_array(const OctaveBase &octarr,
+                                      int pyarraytype, boost::true_type) {
+      return create_array<PythonPrimitive, OctaveBase> (octarr, pyarraytype);
+   }
+
+   template <class PythonPrimitive, class OctaveBase>
+   static PyArrayObject *create_array(const OctaveBase &octarr,
+                                      int pyarraytype, boost::false_type) {
+      assert(0);
+      return 0;
+   }
+
    template <class CLASS, size_t bytes>
    inline static PyArrayObject *create_uint_array(CLASS value) {
       if (bytes == sizeof(int)) {
-         return create_array<unsigned int, CLASS>(value, PyArray_UINT);
+         boost::integral_constant<bool, bytes == sizeof(int)> inst;
+         return create_array<unsigned int, CLASS>(value, PyArray_UINT, inst);
       } else if (bytes == sizeof(char)) {
-         return create_array<unsigned char, CLASS>(value, PyArray_UBYTE);
+         boost::integral_constant<bool, bytes == sizeof(char)> inst;
+         return create_array<unsigned char, CLASS>(value, PyArray_UBYTE, inst);
       } else if (bytes == sizeof(short)) {
-         return create_array<unsigned short, CLASS>(value, PyArray_USHORT);
+         boost::integral_constant<bool,
+            bytes == sizeof(short) && bytes != sizeof(int)> inst;
+         return create_array<unsigned short, CLASS>(value, PyArray_USHORT, inst);
       } else {
          ostringstream os;
          os << "Numeric arrays doesn't support unsigned " << (bytes*8)
@@ -175,14 +166,22 @@
 
    template <class CLASS, size_t bytes>
    inline static PyArrayObject *create_sint_array(CLASS value) {
-      if (bytes == sizeof(long)) {
-         return create_array<long, CLASS>(value, PyArray_LONG);
-      } else if (bytes == sizeof(int)) {
-         return create_array<signed int, CLASS>(value, PyArray_INT);
+      if (bytes == sizeof(int)) {
+         // We test int first since we prefer int to other datatypes of the
+         // same size.
+         boost::integral_constant<bool, bytes == sizeof(int)> inst;
+         return create_array<signed int, CLASS>(value, PyArray_INT, inst);
+      } else if (bytes == sizeof(long)) {
+         boost::integral_constant<bool,
+            bytes==sizeof(long) && bytes != sizeof(int)> inst;
+         return create_array<long, CLASS>(value, PyArray_LONG, inst);
       } else if (bytes == sizeof(char)) {
-         return create_array<signed char, CLASS>(value, PyArray_SBYTE);
+         boost::integral_constant<bool, bytes == sizeof(char)> inst;
+         return create_array<signed char, CLASS>(value, PyArray_SBYTE, inst);
       } else if (bytes == sizeof(short)) {
-         return create_array<signed short, CLASS>(value, PyArray_SHORT);
+         boost::integral_constant<bool,
+            bytes==sizeof(short) && bytes != sizeof(int)> inst;
+         return create_array<signed short, CLASS>(value, PyArray_SHORT, inst);
       } else {
          ostringstream os;
          os << "Numeric arrays doesn't support signed " << (bytes*8)
@@ -192,28 +191,27 @@
    }
 
    static PyArrayObject *octvalue_to_pyarrobj(const octave_value &matrix) {
-      if (matrix.is_complex_type ()) {
-            throw value_convert_exception(
-               "Complex Octave matrices conversion not implemented");
-      }
-
       if (matrix.is_double_type ()) {
-         if (matrix.is_real_type()) {
+         if (matrix.is_complex_type ()) {
+            return create_array<Complex, ComplexNDArray>
+               (matrix.complex_array_value(), PyArray_CDOUBLE);
+         } else if (matrix.is_real_type()) {
             return create_array<double, NDArray>(matrix.array_value(),
                                                  PyArray_DOUBLE);
          } else
             throw value_convert_exception("Unknown double matrix type");
       }
 
-#ifdef PYTAVE_USE_OCTAVE_FLOATS
       if (matrix.is_single_type ()) {
-         if (matrix.is_real_type()) {
+         if (matrix.is_complex_type ()) {
+            return create_array<FloatComplex, FloatComplexNDArray>
+               (matrix.float_complex_array_value(), PyArray_CFLOAT);
+         } else if (matrix.is_real_type()) {
             return create_array<float, FloatNDArray>(
                matrix.float_array_value(), PyArray_FLOAT);
          } else
             throw value_convert_exception("Unknown float matrix type");
       }
-#endif
 
       if (matrix.is_int64_type()) {
          return create_sint_array<int64NDArray, sizeof(int64_t)>(
@@ -243,6 +241,25 @@
          return create_sint_array<int8NDArray, sizeof(int8_t)>(
             matrix.int8_array_value());
       }
+      if (matrix.is_bool_type()) {
+#ifdef HAVE_NUMPY
+         // NumPY has logical arrays, and even provides an old-style #define.
+         return create_array<bool, boolNDArray>(
+            matrix.bool_array_value(), PyArray_BOOL);
+#else
+         // Numeric does not support bools, use uint8.
+         return create_uint_array<uint8NDArray, sizeof(uint8_t)>(
+            matrix.uint8_array_value());
+#endif
+      }
+      if (matrix.is_string()) {
+         return create_array<char, charNDArray>(
+            matrix.char_array_value(), PyArray_CHAR);
+      }
+      if (matrix.is_cell()) {
+         return create_array<PyObject *, Cell>(
+            matrix.cell_value(), PyArray_OBJECT);
+      }
 
       throw value_convert_exception("Octave matrix type not known, "
                                     "conversion not implemented");
@@ -254,25 +271,6 @@
       py_object = object(handle<PyObject>((PyObject *)pyarr));
    }
 
-
-   static void octcell_to_pyobject(boost::python::object &py_object,
-                                   const Cell& cell) {
-      py_object = boost::python::list();
-
-      if(cell.dim1() != 1) {
-         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));
-
-         ((boost::python::list&)py_object).insert(i, py_val);
-      }
-   }
-
    static void octmap_to_pyobject(boost::python::object &py_object,
                                   const Octave_map& map) {
       py_object = boost::python::dict();
@@ -281,7 +279,9 @@
       for(octave_idx_type i = 0 ; i < keys.length(); i++) {
          boost::python::object py_val;
 
-         octvalue_to_pyobj(py_val, map.contents(keys[i]));
+         const Cell c = map.contents(keys[i]);
+
+         octvalue_to_pyarr(py_val, c);
 
          py_object[keys[i]] = py_val;
       }
@@ -289,30 +289,14 @@
 
    void octvalue_to_pyobj(boost::python::object &py_object,
                           const octave_value& octvalue) {
-      if (octvalue.is_undefined())
+      if (octvalue.is_undefined()) {
          throw value_convert_exception(
             "Octave value `undefined'. Can not convert to a Python object");
-      else if (octvalue.is_scalar_type()) {
-         if (octvalue.is_bool_type())
-            py_object = object(octvalue.bool_value());
-         else if (octvalue.is_real_scalar())
-            py_object = object(octvalue.double_value());
-         else if (octvalue.is_integer_type())
-            py_object = object(octvalue.int_value());
-         else
-            throw value_convert_exception(
-               "Conversion for this scalar not implemented");
-      } else if (octvalue.is_string()) {
-         if (octvalue.all_strings().dim1() > 1)
-            throw value_convert_exception(
-               "Multi-row character matrices can not be converted.");
-         py_object = str(octvalue.string_value());
-      } else if (octvalue.is_matrix_type()) {
+      } else if (octvalue.is_numeric_type() || octvalue.is_string()
+                 || octvalue.is_cell()) {
          octvalue_to_pyarr(py_object, octvalue);
       } else if (octvalue.is_map()) {
          octmap_to_pyobject(py_object, octvalue.map_value());
-      } else if (octvalue.is_cell()) {
-         octcell_to_pyobject(py_object, octvalue.cell_value()); 
       } else
          throw value_convert_exception(
             "Conversion from Octave value not implemented");
--- a/octave_to_python.h	Sat May 09 15:50:16 2009 +0200
+++ b/octave_to_python.h	Mon Aug 24 11:02:29 2009 +0200
@@ -21,6 +21,8 @@
 #define OCTAVE_TO_PYTHON_H
 
 namespace pytave {
+   void octvalue_to_pyobj(boost::python::object &py_object,
+                          const octave_value& octvalue);
    void octlist_to_pytuple(boost::python::tuple &python_tuple,
                            const octave_value_list &octave_list);
 }
--- a/package/pytave.py	Sat May 09 15:50:16 2009 +0200
+++ b/package/pytave.py	Mon Aug 24 11:02:29 2009 +0200
@@ -1,6 +1,7 @@
 # -*- coding:utf-8 -*-
 #
 # Copyright 2008 David Grundberg, Håkan Fors Nilsson
+# Copyright 2009 Jaroslav Hajek, VZLU Prague
 #
 # This file is part of Pytave.
 #
@@ -19,11 +20,34 @@
 
 """Python to Octave bridge"""
 
+import UserDict
 import _pytave
+import atexit
+import sys
+
+arg0 = sys.argv[0]
+interactive = sys.stdin.isatty() and (arg0 == '' or arg0 == '-')
+
+_pytave.init(interactive)
+(OctaveError, ValueConvertError, ObjectConvertError, ParseError, \
+ VarNameError) = _pytave.get_exceptions();
 
-_pytave.init()
-(OctaveError, ValueConvertError, ObjectConvertError, ParseError) \
-				  = _pytave.get_exceptions();
+# Dynamic import. *Must* go after _pytave.init() !
+__modname__ = _pytave.get_module_name()
+if __modname__ == 'numpy':
+    from numpy import oldnumeric as Numeric
+elif __modname__ == 'Numeric':
+    import Numeric
+elif __modname__ == 'numarray':
+    # FIXME: Is this OK?
+    import numarray as Numeric
+else:
+    raise ImportError("Failed to import module: %s" % __modname__)
+
+def _atexit():
+	_pytave.atexit()
+
+atexit.register(_atexit)
 
 def feval(nargout, funcname, *arguments):
 
@@ -49,9 +73,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
@@ -59,29 +83,37 @@
 		UINT, SINT,         -''-
 		LONG,               -''-
 		DOUBLE              -''-
+		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. The behavior is
+	analogical to the Octave "struct" function: values that
+	evaluate to cells must have matching dimensions, singleton
+	cells and non-cell values are expanded.
 	
 	Octave to Python
 	================
 	
-	Scalar values to objects:
-		bool                bool
-		real scalar         float (64-bit)
-		any string*         str
-		struct              dict
-		cell*               list
+        All scalar values are regarded as 1x1 matrices, as they are in
+	Octave.
 
-		* Cell arrays must be one-dimensional (row vector) and
-                  character matrices must only have one row.  Any
-                  other form will raise a ValueConvertError.
-		
 	Matrix values to Numeric arrays:
+	   	double              DOUBLE
+		single              FLOAT
+		logical             DOUBLE
 		int64               LONG
 		int32, uint32       INT, UINT
 		int16, uint16       SHORT, USHORT
 		int8, unint8        SBYTE, UBYTE
+		char                CHAR
+		cell                OBJECT
+
+	Structs are converted to dicts, where each value is an OBJECT
+	array.
 
 	All other values causes a pytave.ValueConvertError to be
 	raised. This exception inherits TypeError.
@@ -131,6 +163,63 @@
 
 	return _pytave.eval(nargout, code, silent)
 
+def stripdict(dictarray):
+    """A helper function to convert structures obtained from Octave.
+    Because in Octave, all structs are also arrays, they are returned
+    as dicts of object arrays. In the common case of a 1x1 struct, 
+    stripdict strips the values."""
+    
+    sdict = {}
+    for key in dictarray:
+	sdict[key] = dictarray[key][0,0]
+    return sdict
+
+def narrowlist(objarray):
+    """A helper function to convert cell arrays obtained from Octave.
+    Octave cells are returned as Numeric object arrays. This function
+    will flatten the array and convert it into a 1D list."""
+
+    return Numeric.ravel(objarray).tolist()
+
+def simplify(obj):
+    """A helper function to convert results obtained from Octave.
+    This will convert all 1x1 arrays to scalars, vectors to 1D arrays,
+    1xN and 0x0 character arrays to strings, 1xN, Nx1 and 0x0 cell
+    arrays to lists, and strip scalar dicts. It will work recursively."""
+
+    def vectordims(dims,column_allowed = True):
+	return (len(dims) == 2 and 
+		((dims[0] == 1 or (column_allowed and dims[1] == 1)) or
+		(dims[0] == 0 and dims[1] == 0)))
+
+    if isinstance(obj,Numeric.ArrayType):
+	tc = obj.typecode()
+	if tc == 'O':
+	    if vectordims(Numeric.shape(obj)):
+		return map(simplify,narrowlist(obj))
+	elif tc == 'c':
+	    if vectordims(Numeric.shape(obj), False):
+		return obj.tostring()
+	else:
+	    dims = Numeric.shape(obj)
+	    if dims == (1,1):
+		return obj.toscalar()
+	    elif vectordims(dims):
+		return Numeric.ravel(obj)
+    elif isinstance(obj,dict):
+	sobj = {}
+	for key in obj:
+	    sval = simplify(obj[key])
+	    if isinstance(sval,list) and len(sval) == 1:
+		sval = sval[0]
+	    sobj[key] = sval
+	return sobj
+    elif isinstance(obj,tuple):
+	return tuple(map(simplify,obj))
+    ## default.
+    return obj
+
+
 def addpath(*arguments):
 	"""See Octave documentation"""
 	return _pytave.feval(1, "addpath", arguments)[0]
@@ -143,11 +232,103 @@
 	"""See Octave documentation"""
 	return _pytave.feval(1, "path", paths)[0]
 
+class _VariablesDict(UserDict.DictMixin):
+	def __init__(self, global_variables, native=False):
+		self.global_variables = global_variables
+		self.native = native
+
+	def __getitem__(self, name):
+		if not isinstance(name, basestring):
+			raise TypeError('Expected a string, not a ' + repr(type(name)))
+		try:
+			return _pytave.getvar(name, self.global_variables)
+		except VarNameError:
+			raise KeyError('No Octave variable named ' + name)
+
+	def __setitem__(self, name, value):
+		if not isinstance(name, basestring):
+			raise TypeError('Expected a string, not a ' + repr(type(name)))
+		_pytave.setvar(name, value, self.global_variables)
+
+	def __contains__(self, name):
+		if not isinstance(name, basestring):
+			raise TypeError('Expected a string, not a ' + repr(type(name)))
+		return _pytave.isvar(name, self.global_variables)
+
+	def __delitem__(self, name):
+		if not isinstance(name, basestring):
+			raise TypeError('Expected a string, not a ' + repr(type(name)))
+		# 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)
+globals = _VariablesDict(global_variables=True)
+
+def push_scope():
+	 """Creates a new anonymous local variable scope on the Octave call
+	 stack and sets it as the current Octave scope. Subsequent eval,
+	 getvar and setvar calls will affect variables within this scope.
+
+	 This is useful to do if you call the Octave engine from within
+	 multiple Python functions, to prevent them from hampering each
+	 other's data. As such, it is advisable to always create a local
+	 scope in a production code.
+	 """
+	 return _pytave.push_scope()
+
+def pop_scope():
+	 """Pops the current active scope (created previously by
+	 push_scope) off the Octave call stack. The previous scope
+	 will become the active scope.
+
+	 If already at the top-level scope, this function does nothing.
+	 """
+	 _pytave.pop_scope()
+ 
+class _LocalScope:
+	 def __init__(self, func):
+		  self.func = func
+		  self.__name__ = func.__name__
+		  self.__doc__ = func.__doc__
+
+	 def __call__(self, *args, **kwargs):
+		  try:
+				_pytave.push_scope()
+				return self.func(*args, **kwargs)
+		  finally:
+				_pytave.pop_scope()
+
+def local_scope(func):
+	 """Decorates a function to use local Octave scope.
+	 Example:
+
+	 @pytave.local_scope
+	 def myfunc(a,b):
+		  <function body>
+
+	 is equivalent to:
+
+	 def myfunc(a,b):
+		  try:
+				pytave.push_scope()
+				<function body>
+		  finally:
+			  pytave.pop_scope()
+	 """
+	 return _LocalScope(func)
+
 # Emacs
 #	Local Variables:
 #	fill-column:70
 #	coding:utf-8
 #	indent-tabs-mode:t
 #	tab-width:8
+#	python-indent:8
 #	End:
-# vim: set textwidth=70 noexpandtab tabstop=3 :
+# vim: set textwidth=70 noexpandtab tabstop=8 :
--- a/pytave.cc	Sat May 09 15:50:16 2009 +0200
+++ b/pytave.cc	Mon Aug 24 11:02:29 2009 +0200
@@ -1,5 +1,6 @@
 /*
  *  Copyright 2008 David Grundberg, Håkan Fors Nilsson
+ *  Copyright 2009 Jaroslav Hajek, VZLU Prague
  *
  *  This file is part of Pytave.
  *
@@ -23,33 +24,49 @@
 #include <boost/python/numeric.hpp>
 
 #undef HAVE_STAT /* Both boost.python and octave define HAVE_STAT... */
+#undef HAVE_FSTAT /* Both boost.python and octave define HAVE_FSTAT... */
 #include <octave/oct.h>
+
 #include <octave/oct-map.h>
 #include <octave/octave.h>
 #include <octave/ov.h>
 #include <octave/parse.h>
+#include <octave/symtab.h>
+#include <octave/toplev.h>
+#include <octave/utils.h>
 
 #include <iostream>
+#ifdef HAVE_USELOCALE
+#include <locale.h>
+#endif
 #include <sstream>
 #include <sys/types.h>
 
 #include "pytavedefs.h"
 
 #include "exceptions.h"
+#include "octave_to_python.h"
 #include "python_to_octave.h"
-#include "octave_to_python.h"
 
 using namespace boost::python;
 using namespace std;
 
-namespace pytave { /* {{{ */ 
+namespace pytave { /* {{{ */
 
-   void init() {
+#ifdef HAVE_USELOCALE
+   locale_t c_locale;
+#endif
+
+   void init(bool silent = true) {
+#ifdef HAVE_USELOCALE
+      c_locale = newlocale(LC_ALL, "C", 0);
+#endif
 
       if (!octave_error_exception::init()
           || !value_convert_exception::init()
           || !object_convert_exception::init()
-          || !octave_parse_exception::init()) {
+          || !octave_parse_exception::init()
+          || !variable_name_exception::init ()) {
          PyErr_SetString(PyExc_ImportError, "_pytave: init failed");
          return;
       }
@@ -59,14 +76,39 @@
       const char* argv[] = {"octave",
                             "--no-line-editing",
                             "--no-history",
+                            "--silent",
                             NULL};
-      octave_main(3, const_cast<char**>(argv), 1);
+      int argc = 4;
+
+      if (silent) {
+         argv[3] = 0;
+         argc = 3;
+      }
+
+#ifdef HAVE_USELOCALE
+      // Set C locale
+      locale_t old_locale = uselocale(c_locale);
+#endif
+
+      octave_main(argc, const_cast<char**>(argv), 1);
+
+#ifdef HAVE_USELOCALE
+      // Reset locale
+      uselocale(old_locale);
+#endif
 
       // Initialize Python Numeric Array
 
       // This is actually a macro that becomes a block expression. If an error
       // occurs, e.g. Numeric Array not installed, an exception is set.
       import_array()
+#ifdef HAVE_NUMPY
+      numeric::array::set_module_and_type ("numpy", "ndarray");
+#endif
+   }
+
+   string get_module_name () {
+      return numeric::array::get_module_name ();
    }
 
    boost::python::tuple get_exceptions() {
@@ -77,7 +119,9 @@
                         object(handle<PyObject>(
                                   object_convert_exception::excclass)),
                         object(handle<PyObject>(
-                                  octave_parse_exception::excclass)));
+                                  octave_parse_exception::excclass)),
+                        object(handle<PyObject>(
+                                  variable_name_exception::excclass)));
    }
 
    string make_error_message (const Octave_map& map) {
@@ -111,7 +155,6 @@
       return exceptionmsg.str ();
    }
 
-     
    boost::python::tuple func_eval(const int nargout,
                                   const string &funcname,
                                   const boost::python::tuple &arguments) {
@@ -122,11 +165,24 @@
 
       reset_error_handler();
       buffer_error_messages++;
-      
+
+      // Updating the timestamp makes Octave reread changed files
+      Vlast_prompt_time.stamp();
+
+#ifdef HAVE_USELOCALE
+      // Set C locale
+      locale_t old_locale = uselocale(c_locale);
+#endif
+
       Py_BEGIN_ALLOW_THREADS
       retval = feval(funcname, octave_args, (nargout >= 0) ? nargout : 0);
       Py_END_ALLOW_THREADS
 
+#ifdef HAVE_USELOCALE
+      // Reset locale
+      uselocale(old_locale);
+#endif
+
       if (error_state != 0) {
 // error_state values:
 // -2 error without traceback
@@ -162,12 +218,25 @@
 
       reset_error_handler();
       buffer_error_messages++;
-      
+
+      // Updating the timestamp makes Octave reread changed files
+      Vlast_prompt_time.stamp();
+
+#ifdef HAVE_USELOCALE
+      // Set C locale
+      locale_t old_locale = uselocale(c_locale);
+#endif
+
       Py_BEGIN_ALLOW_THREADS
       retval = eval_string(code, silent, parse_status,
          (nargout >= 0) ? nargout : 0);
       Py_END_ALLOW_THREADS
 
+#ifdef HAVE_USELOCALE
+      // Reset locale
+      uselocale(old_locale);
+#endif
+
       if (parse_status != 0 || error_state != 0) {
 // error_state values:
 // -2 error without traceback
@@ -197,14 +266,122 @@
          return make_tuple();
       }
    }
+
+   boost::python::object getvar(const string& name,
+                                bool global) {
+      octave_value val;
+
+      if (global)
+         val = symbol_table::global_varval(name);
+      else
+         val = symbol_table::varval(name);
+
+      if (val.is_undefined()) {
+         throw variable_name_exception (name + " not defined in current scope");
+      }
+
+      boost::python::object pyobject;
+      octvalue_to_pyobj(pyobject, val);
+
+      return pyobject;
+   }
+
+   void setvar(const string& name, 
+               const boost::python::object& pyobject,
+               bool global) {
+      octave_value val;
+
+      if (!valid_identifier(name)) {
+         throw variable_name_exception (name + " is not a valid identifier");
+      }
+
+      pyobj_to_octvalue(val, pyobject);
+
+      if (global)
+         symbol_table::global_varref(name) = val;
+      else
+         symbol_table::varref(name) = val;
+   }
+
+   bool isvar(const string& name, bool global) {
+      bool retval;
+
+      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);
+      octave_call_stack::push(local_scope);
+      return local_scope;
+   }
+
+   void pop_scope () {
+      symbol_table::scope_id curr_scope = symbol_table::current_scope();
+      if (curr_scope != symbol_table::top_scope())
+         {
+            symbol_table::erase_scope(curr_scope);
+            octave_call_stack::pop();
+         }
+   }
+
+// Make sure Octave is correctly unloaded. We cannot depend on Octave running
+// at the (true) process atexit point, because at that time the Octave library
+// might have been unloaded.
+//
+// At least that is the hypothesis, since Octave (in certain circumstances)
+// cause a segmentation fault in do_octave_atexit called from the exit
+// function. (One Octave call that causes this is "sleep(0)".)
+   void atexit () {
+#ifdef HAVE_USELOCALE
+      // Set C locale
+      locale_t old_locale = uselocale(c_locale);
+#endif
+
+      Py_BEGIN_ALLOW_THREADS
+      do_octave_atexit();
+      Py_END_ALLOW_THREADS
+
+#ifdef HAVE_USELOCALE
+      // Reset locale
+      uselocale(old_locale);
+#endif
+   }
+
 } /* namespace pytave }}} */
 
 BOOST_PYTHON_MODULE(_pytave) { /* {{{ */
    using namespace boost::python;
 
    def("init", pytave::init);
+   def("get_module_name", pytave::get_module_name);
    def("feval", pytave::func_eval);
    def("eval", pytave::str_eval);
+   def("getvar", pytave::getvar);
+   def("setvar", pytave::setvar);
+   def("isvar", pytave::isvar);
+   def("delvar", pytave::delvar);
+   def("push_scope", pytave::push_scope);
+   def("pop_scope", pytave::pop_scope);
+   def("atexit", pytave::atexit);
    def("get_exceptions", pytave::get_exceptions);
 
    register_exception_translator<pytave::pytave_exception>(
@@ -222,6 +399,9 @@
    register_exception_translator<pytave::value_convert_exception>(
       pytave::value_convert_exception::translate_exception);
 
+   register_exception_translator<pytave::variable_name_exception>(
+      pytave::variable_name_exception::translate_exception);
+
 } /* }}} */
 
 /* Emacs
--- a/python_to_octave.cc	Sat May 09 15:50:16 2009 +0200
+++ b/python_to_octave.cc	Mon Aug 24 11:02:29 2009 +0200
@@ -1,5 +1,6 @@
 /*
  *  Copyright 2008 David Grundberg, Håkan Fors Nilsson
+ *  Copyright 2009 VZLU Prague
  *
  *  This file is part of Pytave.
  *
@@ -21,6 +22,7 @@
 #include <boost/python.hpp>
 #include <boost/python/numeric.hpp>
 #include "arrayobjectdefs.h"
+#include <boost/type_traits/integral_constant.hpp>
 #undef HAVE_STAT /* both boost.python and octave defines HAVE_STAT... */
 #include <octave/oct.h>
 #include <octave/oct-map.h>
@@ -67,17 +69,96 @@
       }
    }
 
+   template <>
+   void copy_pyarrobj_to_octarray<PyObject *, Cell>(Cell &matrix,
+                                  const PyArrayObject* const pyarr,
+                                  const int unsigned matindex,
+                                  const unsigned int matstride,
+                                  const int dimension,
+                                  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++) {
+            PyObject *pobj = *(PyObject **)
+               &ptr[offset + i*pyarr->strides[dimension]];
+            pyobj_to_octvalue (matrix.elem(matindex + i*matstride), 
+                               object(handle<PyObject> (borrowed (pobj))));
+         }
+      } else {
+         for (int i = 0; i < pyarr->dimensions[dimension]; i++) {
+            copy_pyarrobj_to_octarray<PyObject *, Cell>(
+               matrix,
+               pyarr,
+               matindex + i*matstride,
+               matstride * pyarr->dimensions[dimension],
+               dimension + 1,
+               offset + i*pyarr->strides[dimension]);
+         }
+      }
+   }
+
+   template <class PythonPrimitive, class OctaveBase>
+   static void copy_pyarrobj_to_octarray_dispatch(OctaveBase &matrix,
+                                       const PyArrayObject* const pyarr,
+                                       const boost::true_type&) {
+      copy_pyarrobj_to_octarray<PythonPrimitive, OctaveBase>
+         (matrix, pyarr, 0, 1, 0, 0);
+   }
+
+   template <class PythonPrimitive, class OctaveBase>
+   static void copy_pyarrobj_to_octarray_dispatch(OctaveBase &matrix,
+                                       const PyArrayObject* const pyarr,
+                                       const boost::false_type&) {
+      assert(0);
+   }
+
+   template <class X, class Y> class matching_type : public boost::false_type { };
+   template <class X> class matching_type<X, X> : public boost::true_type { };
+   template <class X> class matching_type<X, octave_int<X> > : public boost::true_type { };
+   template <> class matching_type<float, double> : public boost::true_type { };
+   template <> class matching_type<FloatComplex, Complex> : public boost::true_type { };
+   template <> class matching_type<PyObject *, octave_value> : public boost::true_type { };
+
+   template <class PythonPrimitive, class OctaveBase>
+   static void copy_pyarrobj_to_octarray_dispatch(OctaveBase &matrix,
+                                       const PyArrayObject* const pyarr) {
+      matching_type<PythonPrimitive, typename OctaveBase::element_type> inst;
+      copy_pyarrobj_to_octarray_dispatch<PythonPrimitive, OctaveBase> (matrix, pyarr, inst);
+   }
+
    template <class OctaveBase>
    static void copy_pyarrobj_to_octarray_boot(OctaveBase &matrix,
                                        const PyArrayObject* const pyarr) {
 
 #define ARRAYCASE(AC_pyarrtype, AC_primitive) case AC_pyarrtype: \
-         copy_pyarrobj_to_octarray<AC_primitive, OctaveBase>\
-         (matrix, pyarr, 0, 1, 0, 0); \
+         copy_pyarrobj_to_octarray_dispatch<AC_primitive, OctaveBase>\
+         (matrix, pyarr); \
          break; \
 
-      switch (pyarr->descr->type_num) {
-//         ARRAYCASE(PyArray_CHAR, )
+      // Prefer int to other types of the same size.
+      // E.g. on 32-bit x86 architectures: sizeof(long) == sizeof(int).
+      int type_num = pyarr->descr->type_num;
+      switch (type_num) {
+         case PyArray_LONG:
+            if (sizeof(long) == sizeof(int)) {
+               type_num = PyArray_INT;
+            }
+            break;
+         case PyArray_SHORT:
+            if (sizeof(short) == sizeof(int)) {
+               type_num = PyArray_INT;
+            }
+            break;
+         case PyArray_USHORT:
+            if (sizeof(unsigned short) == sizeof(unsigned int)) {
+               type_num = PyArray_UINT;
+            }
+            break;
+      }
+
+      switch (type_num) {
+         ARRAYCASE(PyArray_CHAR,            char)
          ARRAYCASE(PyArray_UBYTE,  unsigned char)
          ARRAYCASE(PyArray_SBYTE,  signed   char)
          ARRAYCASE(PyArray_SHORT,  signed   short)
@@ -91,9 +172,19 @@
 
          /* Commonly Numeric.array(..., Numeric.Float) */
          ARRAYCASE(PyArray_DOUBLE, double)
-//         ARRAYCASE(PyArray_CFLOAT, )
-//         ARRAYCASE(PyArray_CDOUBLE, )
-//         ARRAYCASE(PyArray_OBJECT, )
+
+         /* Commonly Numeric.array(..., Numeric.Complex32) */
+         ARRAYCASE(PyArray_CFLOAT, FloatComplex)
+
+         /* Commonly Numeric.array(..., Numeric.Complex) */
+         ARRAYCASE(PyArray_CDOUBLE, Complex)
+
+#ifdef HAVE_NUMPY
+         ARRAYCASE(PyArray_BOOL, bool)
+#endif
+
+         ARRAYCASE(PyArray_OBJECT, PyObject *)
+
          default:
             throw object_convert_exception(
                PyEval_GetFuncName((PyObject*)pyarr)
@@ -168,15 +259,30 @@
             }
             break;
          case PyArray_FLOAT:
-#ifdef PYTAVE_USE_OCTAVE_FLOATS
             pyarrobj_to_octvalueNd<FloatNDArray>(octvalue, pyarr, dims);
             break;
-#else
-            /* fallthrough */
-#endif
          case PyArray_DOUBLE:
             pyarrobj_to_octvalueNd<NDArray>(octvalue, pyarr, dims);
             break;
+         case PyArray_CFLOAT:
+            pyarrobj_to_octvalueNd<FloatComplexNDArray>(octvalue, pyarr, dims);
+            break;
+         case PyArray_CDOUBLE:
+            pyarrobj_to_octvalueNd<ComplexNDArray>(octvalue, pyarr, dims);
+            break;
+         case PyArray_CHAR:
+            pyarrobj_to_octvalueNd<charNDArray>(octvalue, pyarr, dims);
+            // FIXME: is the following needed?
+            octvalue = octvalue.convert_to_str(true, true, '"');
+            break;
+#ifdef HAVE_NUMPY
+         case PyArray_BOOL:
+            pyarrobj_to_octvalueNd<boolNDArray>(octvalue, pyarr, dims);
+            break;
+#endif
+         case PyArray_OBJECT:
+            pyarrobj_to_octvalueNd<Cell>(octvalue, pyarr, dims);
+            break;
          default:
             throw object_convert_exception(
                PyEval_GetFuncDesc((PyObject*)(pyarr)) + string(" ")
@@ -213,46 +319,15 @@
 
       dim_vector dims = dim_vector(1, 1);
 
-      bool has_dimensions = false;
-
-      for(octave_idx_type i = 0; i < length; i++) {
-         octave_value val;
-
-         boost::python::tuple tuple =
-            boost::python::extract<boost::python::tuple>(list[i])();
-
-         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");
-
-            // Some things are assumed since we have converted a Python list to
-            // a cell.
-            assert(c.dims().length() == 2);
-            assert(c.dim1() == 1);
+      Array<octave_value> vals (length);
+      Array<std::string> keys (length);
 
-            // 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)) {
+      // Extract all keys and convert values. Remember whether dimensions
+      // match.
+      
+      for(octave_idx_type i = 0; i < length; i++) {
 
-               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");
-               }
-            }
-         }
-      }
-
-      Octave_map map = Octave_map(dims);
-
-      for(octave_idx_type i = 0; i < length; i++) {
-         octave_value val;
-         std::string key;
+         std::string& key = keys(i);
 
          boost::python::tuple tuple =
             boost::python::extract<boost::python::tuple>(list[i])();
@@ -274,26 +349,36 @@
                "field name. Field names must be valid Octave identifiers.");
          }
 
-         // FIXME: Second time around we convert exactly the same object
+         octave_value& val = vals(i);
+
          pyobj_to_octvalue(val, tuple[1]);
 
-         if(!val.is_cell()) {
-            map.assign(key, Cell(dims, val));
-         } else {
-            const Cell c(val.cell_value());
+         if(val.is_cell()) {
+            if(i == 0) {
+               dims = val.dims();
+            } else if (val.numel() != 1 && val.dims() != dims){
+               throw object_convert_exception(
+                  "Dimensions of the struct fields do not match");
+            }
+         }
+      }
 
-            if (error_state)
-               throw object_convert_exception("Octave error");
+      Octave_map map = Octave_map(dims);
+
+      for(octave_idx_type i = 0; i < length; i++) {
 
-            if(c.dims().length() == 2 && c.dims()(0) == 1 && c.dims()(1) == 1) {
+         std::string& key = keys(i);
+         octave_value val = vals(i);
+
+         if(val.is_cell()) {
+            const Cell c = val.cell_value();
+            if (c.numel () == 1) {
                map.assign(key, Cell(dims, c(0)));
-            }
-            else {
+            } else {
                map.assign(key, c);
             }
-         }
-         if (error_state) {
-            throw object_convert_exception("Octave error");
+         } else {
+            map.assign(key, Cell(dims, val));
          }
       }
       oct_value = map;
@@ -303,6 +388,7 @@
                           const boost::python::object &py_object) {
       extract<int> intx(py_object);
       extract<double> doublex(py_object);
+      extract<Complex> complexx(py_object);
       extract<string> stringx(py_object);
       extract<numeric::array> arrayx(py_object);
       extract<boost::python::list> listx(py_object);
@@ -311,6 +397,8 @@
          oct_value = intx();
       } else if (doublex.check()) {
          oct_value = doublex();
+      } else if (complexx.check()) {
+         oct_value = complexx();
       } else if (arrayx.check()) {
          pyarr_to_octvalue(oct_value, (PyArrayObject*)py_object.ptr());
       } else if (stringx.check()) {
--- a/python_to_octave.h	Sat May 09 15:50:16 2009 +0200
+++ b/python_to_octave.h	Mon Aug 24 11:02:29 2009 +0200
@@ -21,6 +21,8 @@
 #define PYTHON_TO_OCTAVE_H
 
 namespace pytave {
+   void pyobj_to_octvalue(octave_value &oct_value,
+                          const boost::python::object &py_object);
    void pytuple_to_octlist(octave_value_list &octave_list,
                            const boost::python::tuple &python_tuple);
 }
--- a/setup.py.in	Sat May 09 15:50:16 2009 +0200
+++ b/setup.py.in	Mon Aug 24 11:02:29 2009 +0200
@@ -1,8 +1,20 @@
-#!/usr/bin/python
+#!@PYTHON@
 # -*- coding: utf-8; c-basic-offset: 3; indent-tabs-mode: nil; tab-width: 3; -*-
 # @configure_input@
 
-from distutils.core import setup, Extension
+from distutils.core import setup, Extension, DistutilsModuleError
+
+include_dirs = ['/usr/local/include/octave-3.1.55', 
+	        '/home/hajek/devel/pytave-repo/pytave', '.'] # Python always included.
+
+# check for numpy. If it exists, define the path.
+
+if '@pytave_enable_numpy@' == 'yes':
+    try:
+        from numpy import get_include
+        include_dirs.append(get_include())
+    except ImportError:
+        raise DistutilsModuleError("could not found numpy")
 
 setup(
    name = 'pytave',
@@ -31,8 +43,8 @@
          
          # TODO: Check whether paths work on Windows or not.
          # The file separator might be wrong. (Must be / in setup.cfg)
-         include_dirs = ['@OCTAVE_INCLUDEDIR@', '@abs_builddir@', '@srcdir@'], # Python always included.
-         define_macros = [('HAVE_CONFIG_H', '1')],
+         include_dirs = include_dirs,
+	 define_macros = [('HAVE_CONFIG_H', '1')],
          library_dirs = ['@OCTAVE_LIBRARYDIR@'],
          runtime_library_dirs = ['@PYTAVE_OCTAVE_RPATH@'],
          libraries = ['octinterp', 'octave', 'cruft', '@BOOST_PYTHON_LIB@']
--- a/test/test.py	Sat May 09 15:50:16 2009 +0200
+++ b/test/test.py	Mon Aug 24 11:02:29 2009 +0200
@@ -2,14 +2,15 @@
 # -*- coding:utf-8 -*-
 
 import pytave
-import Numeric
+from pytave import Numeric
+import traceback
 
 print "No messages indicates test pass."
 
 arr0_0 = Numeric.zeros((0,0));
 arr0_1 = Numeric.zeros((0,1));
 arr1_0 = Numeric.zeros((1,0));
-number = Numeric.array([1.32], Numeric.Float32)
+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)
@@ -20,6 +21,12 @@
 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)
+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);
@@ -29,48 +36,57 @@
 
 # This eval call is not to be seen as a encouragement to use Pytave
 # like this. Create a separate .m-file with your complex Octave code.
-pytave.feval(1, "eval", "function [result] = test_return(arg) "
-"result = arg; endfunction")
+pytave.eval(0, "function [result] = test_return(arg); result = arg; endfunction")
 
 pytave.feval(1, "test_return", 1)
 
+def equals(a,b):
+    return Numeric.alltrue(Numeric.ravel(a == b))
+
+def fail(msg, exc=None):
+	print "FAIL:", msg
+	traceback.print_stack()
+	if exc is not None:
+		traceback.print_exc(exc)
+	print ""
+
 def testequal(value):
 	try:
 		nvalue, = pytave.feval(1, "test_return", value)
-		if nvalue != value:
-			print "FAIL as ", value, " != ", nvalue
+		if not equals(value, nvalue):
+			fail("as %s != %s" % (value, nvalue))
 	except TypeError, e:
-		print "FAIL: ", value,":", e
+		fail(value, e)
 
 def testexpect(value, expected):
 	try:
 		nvalue, = pytave.feval(1, "test_return", value)
-		if nvalue != expected:
-			print "FAIL as ", nvalue, " != ", expected, ","
-			print "        sent in", value
+		if not equals(value, nvalue):
+			fail("sent in %s, expecting %s, got %s", (value, expected, nvalue))
 	except TypeError, e:
-		print "FAIL: ", value,":", e
-
+		fail(value, e)
 
 def testmatrix(value):
 	try:
 		nvalue, = pytave.feval(1, "test_return", value)
-#		print "test", (value,)
-#		print "returned ", (nvalue,)
-		class1 = pytave.feval(1, "class", value)
-		class2 = pytave.feval(1, "class", nvalue)
-		if nvalue != value:
-			print "FAIL as ", value, " != ", nvalue
+		class1, = pytave.feval(1, "class", value)
+		class1 = class1.tostring()
+		class2, = pytave.feval(1, "class", nvalue)
+		class2 = class2.tostring()
+		if not equals(value, nvalue):
+			fail("as %s != %s" % (value, nvalue))
 		if value.shape != nvalue.shape:
-			print "Size check failed for: ", (value,) ,". Got ",value.shape, "and later", nvalue.shape, " =++ ", (nvalue,)
+			fail("Size check failed for: %s. Expected shape %s, got %s  with shape %s" \
+			%(value, value.shape, nvalue, nvalue.shape))
 		if class1 != class2:
-			print "Type check failed for: ", (value,) ,". Got ",class1, "and later", class2
+			fail( "Type check failed for: %s. Expected %s. Got %s."
+			%(value, class1, class2))
 	except TypeError, e:
-		print "Execute failed: ", value,":", e
+		fail("Execute failed: %s" % value, e)
 
 def testobjecterror(value):
 	try:
-		print pytave.feval(1, "test_return", value);
+		pytave.feval(1, "test_return", value);
 		print "FAIL:", (value,)
 	except pytave.ObjectConvertError:
 		pass
@@ -79,16 +95,16 @@
 
 def testvalueerror(*value):
 	try:
-		print pytave.feval(1, *value);
-		print "FAIL:", (value,)
+		pytave.feval(1, *value);
+		fail(value)
 	except pytave.ValueConvertError:
 		pass
 	except Exception, e:
-		print "FAIL", (value,), e
+		fail(value, e)
 
 def testparseerror(*value):
 	try:
-		print pytave.eval(*value);
+		pytave.eval(*value);
 		print "FAIL:", (value,)
 	except pytave.ParseError:
 		pass
@@ -104,14 +120,61 @@
 def testevalexpect(numargout, code, expectations):
 	try:
 		results = pytave.eval(numargout, code);
-		if results != expectations:
-			print "FAIL: eval: ", code, " because", results, " != ", expectations, ","
+		if not equals(results, expectations):
+			fail("eval: %s : because %s != %s" % (code, results, expectations))
+	except Exception, e:
+		fail("eval: %s" % code, e)
+
+def testsetget(variables, name, value):
+	try:
+		variables[name] = value
+		if name not in variables:
+			fail("set/get: %s: Should exist, not there." % name)
+		result, = pytave.feval(1, "isequal", value, variables[name])
+		if not result:
+			fail("set/get: %s -> %s: results diverged" % (name, value))
+	except Exception, e:
+		fail("set/get: %s" % name, e)
+
+def testexception(exception, func):
+	try:
+		func()
+		fail("Expecting %s but nothing was raised." % repr(exception))
 	except Exception, e:
-		print "FAIL: eval:", code, ":", e
-def testcellinvariant(value):
-	pass
+		if not isinstance(e, exception):
+			fail("Expecting %s but got %s instead" % (repr(exception), repr(e)), e)
+
+def testlocalscope(x):
 
-testequal('a')
+    @pytave.local_scope
+    def sloppy_factorial(x):
+	pytave.locals["x"] = x
+	xm1, = pytave.eval(1,"x-1")
+	xm1 = xm1.toscalar()
+	if xm1 > 0:
+	    fxm1 = sloppy_factorial(xm1)
+	else:
+	    fxm1 = 1
+	pytave.locals["fxm1"] = fxm1
+	fx, = pytave.eval(1,"x * fxm1")
+	fx = fx.toscalar()
+	return fx
+
+    try:
+	fx = sloppy_factorial(x)
+	fx1 = 1.0
+	for k in range(1,x+1):
+	    fx1 = k * fx1
+	if fx != fx1:
+	    fail('testlocalscope: result incorrect')
+    except Exception, e:
+	fail("testlocalscope: %s" % (x,), e)
+
+def objarray(obj):
+    return Numeric.array(obj,Numeric.PyObject)
+
+def charray(obj):
+    return Numeric.array(obj,Numeric.Character)
 
 
 testmatrix(alimit_int32)
@@ -119,18 +182,14 @@
 testmatrix(alimit_int8)
 
 # Strings
-# Multi-row character matrix cannot be returned
-testvalueerror("eval", "['foo'; 'bar']")
-testequal('a')
+
+testequal(["mystring"])
+testequal(["mystringåäöÅÄÖ"])
 
-testequal("mystring")
-testequal('mystring')
-testequal("mystringåäöÅÄÖ")
-
-testequal(1)
-testequal(1L)
-testequal(1.2)
-testequal(1.2)
+testexpect(1,Numeric.array([[1]],Numeric.Int))
+if "Int64" in Numeric.__dict__:
+    testexpect(1L,Numeric.array([[1]],Numeric.Int64))
+testexpect(1.0,Numeric.array([[1]],Numeric.Float))
 
 # Vector arrays
 testmatrix(arr1a)
@@ -139,11 +198,12 @@
 testmatrix(arr1fT2)
 testmatrix(arr1i)
 testmatrix(arr1b)
-testmatrix(arr1i32)
+testmatrix(arr1fc)
 
 # 2d arrays
 testmatrix(arr2f)
 testmatrix(arr2d)
+testmatrix(arr2ch)
 
 # 3d arrays
 testmatrix(arr3f)
@@ -153,51 +213,36 @@
 	print "FAIL: Zero test", 
 
 testmatrix(arr0_0)
+testmatrix(arr0_1)
 testmatrix(arr1_0)
-testmatrix(arr0_1)
 
 # Lists
-testequal([1, 2])
-testequal([[1, 2], [3, 4]])
-testequal([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
-testequal([])
+testexpect([1, 2],objarray([[1,2]]))
+testexpect([],objarray([[]]))
 
 # Return cells with OK dimensions
 testvalueok("cell", 1, 3);
 testvalueok("cell", 1, 0)
-
-# Return cells with incompatible dimensions
-testvalueerror("cell", 3, 1)
-testvalueerror("cell", 0, 0)
-testvalueerror("cell", 0, 1)
+testvalueok("cell", 0, 0)
+testvalueok("cell", 3, 1)
+testvalueok("cell", 0, 1)
 
 # Dictionaries
 
-# Simple dictionary tests
-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({})
 
-# 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]})
+# Simple dictionary tests
+testexpect({"foo": 1, "bar": 2},
+	   {"foo": objarray([[1]]), "bar": objarray([[2]])})
+testexpect({"x": [1, 3], "y": [2, 4]},
+	   {"x": objarray([[1,3]]), "y": objarray([[2,4]])})
+# just constructing the second value with Numeric 24.2!
+#testexpect({"x": [1, "baz"], "y": [2, "foobar"]},
+#          {"x": objarray([[1, charray(["baz"])]]), 
+#	   "y": objarray([[2, charray(["foobar"])]])})
+
+testequal({"x": objarray([[arr1f]]), "y": objarray([[arr1i]])})
+testequal({})
+testequal({"foo": arr2o,    "bar": arr2o})
 
 # Try some invalid keys
 testobjecterror({"this is not an Octave identifier": 1})
@@ -209,8 +254,8 @@
 testobjecterror(())
 
 result, = pytave.feval(1, "eval", "[1, 1, 1]")
-if result.shape != (1, 3):
-	print "FAIL: expected 1x3 matrix"
+if result.shape != (1,3):
+	print "FAIL: expected length-3 vector"
 
 result, = pytave.feval(1, "eval", "[1; 2; 3]");
 if result.shape != (3, 1):
@@ -218,5 +263,64 @@
 
 testparseerror(1, "endfunction")
 testevalexpect(1, "2 + 2", (4,))
-testevalexpect(0, "{2}", ([2],))
-testevalexpect(2, "struct('foo', 2)", ({'foo': [2]},))
+testevalexpect(1, "{2}", (objarray([[2]]),))
+testevalexpect(1, "struct('foo', 2)", ({'foo': objarray([[2]])},))
+
+testsetget(pytave.locals, "xxx", arr1f)
+testsetget(pytave.globals, "xxx", arr2o)
+
+def func():
+	pytave.locals["this is not a valid Octave identifier"] = 1
+testexception(pytave.VarNameError, func)
+
+def func():
+	pytave.locals["nonexistentvariable"]
+testexception(KeyError, func)
+
+def func(key):
+	pytave.locals[key] = 1
+testexception(TypeError, lambda: func(0.1))
+testexception(TypeError, lambda: func(1))
+testexception(TypeError, lambda: func([]))
+
+def func(key):
+	pytave.locals[key]
+testexception(TypeError, lambda: func(0.1))
+testexception(TypeError, lambda: func(1))
+testexception(TypeError, lambda: func([]))
+
+testlocalscope(5)
+
+testexception(KeyError, lambda: pytave.locals["localvariable"])
+pytave.locals["localvariable"] = 1
+if "localvariable" in pytave.globals:
+	fail("Local variable in globals")
+del pytave.locals["localvariable"]
+if "localvariable" in pytave.locals:
+	fail("Could not clear local variable")
+testexception(KeyError, lambda: pytave.locals["localvariable"])
+def func():
+	del pytave.locals["localvariable"]
+testexception(KeyError, lambda: func())
+
+testexception(KeyError, lambda: pytave.globals["globalvariable"])
+pytave.globals["globalvariable"] = 1
+if "globalvariable" in pytave.locals:
+	fail("Global variable in locals")
+del pytave.globals["globalvariable"]
+if "globalvariable" in pytave.globals:
+	fail("Could not clear global variable")
+testexception(KeyError, lambda: pytave.globals["globalvariable"])
+def func():
+	del pytave.globals["globalvariable"]
+testexception(KeyError, lambda: func())
+
+# Emacs
+#	Local Variables:
+#	fill-column:70
+#	coding:utf-8
+#	indent-tabs-mode:t
+#	tab-width:8
+#	python-indent:8
+#	End:
+# vim: set textwidth=70 noexpandtab tabstop=8 :