changeset 365:087e7bc3697f

Do not automatically convert Python strings to Octave strings (fixes issue #65) * python_to_octave.cc (pytave::pyobj_to_octvalue): Drop conversion of bytes and unicode objects. * __py_struct_from_dict__.cc (F__py_string_value__): New function. * @pyobject/char.m: Define outside class definition, use __py_string_value__. * @pyobject/pyobject.m: Delete previous pyobject.char definition. * @pyobject/methods.m: Apply char conversion to __name__ attribute. * @py/py.m, @pyobject/cell.m, @pyobject/dummy.m, @pyobject/subsasgn.m, @pyobject/subsref.m, pyargs.m, pycall.cc, pyeval.cc: Adapt examples and tests to changes.
author Mike Miller <mtmiller@octave.org>
date Thu, 25 Aug 2016 14:06:56 -0700
parents 1470ed26917a
children 5c900b8383c4
files @py/py.m @pyobject/cell.m @pyobject/char.m @pyobject/dummy.m @pyobject/methods.m @pyobject/pyobject.m @pyobject/subsasgn.m @pyobject/subsref.m __py_struct_from_dict__.cc pyargs.m pycall.cc pyeval.cc python_to_octave.cc
diffstat 13 files changed, 113 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/@py/py.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/@py/py.m	Thu Aug 25 14:06:56 2016 -0700
@@ -27,7 +27,7 @@
 endfunction
 
 %!assert (py.math.sqrt (2), sqrt (2))
-%!assert (ischar (py.sys.version))
+%!assert (ischar (char (py.sys.version)))
 
 %!test
 %! if (double (py.sys.hexversion) >= 0x03000000)
--- a/@pyobject/cell.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/@pyobject/cell.m	Thu Aug 25 14:06:56 2016 -0700
@@ -42,7 +42,9 @@
 ##     @{
 ##       [1,1] =  10
 ##       [1,2] =  20
-##       [1,3] = hello
+##             = [pyobject ...]
+##
+##                 hello
 ##     @}
 ## @end group
 ## @end example
@@ -84,7 +86,7 @@
 %!assert (cell (pyeval ("[1.]")), {1})
 %!assert (cell (pyeval ("[1., 2., 3.]")), {1, 2, 3})
 %!assert (cell (pyeval ("(1., 2., 3.)")), {1, 2, 3})
-%!assert (cell (pyobject ("asdf")), {"a", "s", "d", "f"})
+%!assert (cellfun (@char, cell (pyobject ("asdf")), "uniformoutput", false), {"a", "s", "d", "f"})
 
 %!test
 %! c = cell (pyeval ("range(10)"));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/@pyobject/char.m	Thu Aug 25 14:06:56 2016 -0700
@@ -0,0 +1,39 @@
+## Copyright (C) 2016 Mike Miller
+##
+## This file is part of Pytave.
+##
+## Pytave is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by the
+## Free Software Foundation; either version 3 of the License, or (at your
+## option) any later version.
+##
+## Pytave is distributed in the hope that it will be useful, but WITHOUT
+## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Pytave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @documentencoding UTF-8
+## @defmethod @@pyobject char (@var{x})
+## Conversion method to string.
+##
+## Example:
+## @example
+## @group
+## s = pyobject ("hello world");
+## char (s)
+##       @result{} hello world
+## @end group
+## @end example
+## @seealso{@@pyobject/disp}
+## @end defmethod
+
+function s = char (x)
+
+  s = __py_string_value__ (x);
+
+endfunction
--- a/@pyobject/dummy.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/@pyobject/dummy.m	Thu Aug 25 14:06:56 2016 -0700
@@ -140,7 +140,8 @@
 ##   @result{} ans =
 ##       @{
 ##         [1,1] =  42
-##         [1,2] = hello
+##               = [pyobject ...]
+##                 hello
 ##               = [pyobject ...]
 ##                 <module 'sys' (built-in)>
 ##       @}
@@ -152,7 +153,7 @@
 ## For example:
 ## @example
 ## @group
-## pycall ("repr", sysmodule)
+## char (pycall ("repr", sysmodule))
 ##   @result{} <module 'sys' (built-in)>
 ## @end group
 ## @end example
--- a/@pyobject/methods.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/@pyobject/methods.m	Thu Aug 25 14:06:56 2016 -0700
@@ -79,12 +79,12 @@
     is_module = pyeval ("lambda x: isinstance(x, __import__('types').ModuleType)");
 
     if (pycall (is_module, x))
-      modulename = pycall ("getattr", x, "__name__");
+      modulename = char (pycall ("getattr", x, "__name__"));
       printf ("Methods for Python module '%s':\n", modulename);
     else
       ## FIXME: should be `class (x)`
       classref = pycall ("getattr", x, "__class__");
-      classname = pycall ("getattr", classref, "__name__");
+      classname = char (pycall ("getattr", classref, "__name__"));
       printf ("Methods for Python class '%s':\n", classname);
     endif
     disp (list_in_columns (mtds_list));
--- a/@pyobject/pyobject.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/@pyobject/pyobject.m	Thu Aug 25 14:06:56 2016 -0700
@@ -77,6 +77,7 @@
     endfunction
 
     # methods defined in external files
+    char (x)
     dummy (x)
     display (x)
     subsref (x, idx)
@@ -85,10 +86,6 @@
       r = x.m_id;
     endfunction
 
-    function s = char (x)
-      s = pycall ("str", x);
-    endfunction
-
     function varargout = disp (x)
       s = char (x);
       if (nargout == 0)
--- a/@pyobject/subsasgn.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/@pyobject/subsasgn.m	Thu Aug 25 14:06:56 2016 -0700
@@ -92,7 +92,7 @@
 %! L{2} = "Octave";
 %! assert (length (L) == 2)
 %! assert (L{1}, 10)
-%! assert (L{2}, "Octave")
+%! assert (char (L{2}), "Octave")
 
 %!test
 %! % dict assignment, adding new keys
--- a/@pyobject/subsref.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/@pyobject/subsref.m	Thu Aug 25 14:06:56 2016 -0700
@@ -140,7 +140,7 @@
 %! [a, b, c] = L{:};
 %! assert (a, 1)
 %! assert (b, 2)
-%! assert (c, "Octave")
+%! assert (char (c), "Octave")
 
 %!test
 %! % 2D array indexing
@@ -174,7 +174,7 @@
 %!test
 %! % dict: floating point keys should work
 %! d = pyeval ("{5.5:'ok'}");
-%! assert (d{5.5}, "ok")
+%! assert (char (d{5.5}), "ok")
 
 %!test
 %! % dict: make sure key ":" doesn't break anything
@@ -209,7 +209,8 @@
 %! pyexec ("import sys")
 %! s = pyeval ("set({sys})");
 %! ver = s.pop ().version;
-%! assert (ischar (ver))
+%! assert (isa (ver, "pyobject"))
+%! assert (ischar (char (ver)))
 
 %!test
 %! % don't set "ans" if no return value
--- a/__py_struct_from_dict__.cc	Thu Aug 25 12:11:02 2016 -0700
+++ b/__py_struct_from_dict__.cc	Thu Aug 25 14:06:56 2016 -0700
@@ -211,6 +211,47 @@
   return ovl (octave_uint64 (key));
 }
 
+DEFUN_DLD (__py_string_value__, args, nargout,
+           "-*- texinfo -*-\n\
+@deftypefn {} {} __py_string_value__ (@var{obj})\n\
+Return the string value or representation of the Python object @var{obj}.\n\
+\n\
+This is a private internal function not intended for direct use.\n\
+@end deftypefn")
+{
+  if (args.length () != 1)
+    print_usage ();
+
+  if (! (args(0).is_object () && args(0).class_name () == "pyobject"))
+    error ("pyobject.char: argument must be a valid Python object");
+
+  Py_Initialize ();
+
+  PyObject *obj = pytave::pyobject_unwrap_object (args(0));
+  if (! obj)
+    error ("pyobject.char: argument must be a valid Python object");
+
+  std::string str;
+
+  if (PyBytes_Check (obj) || PyUnicode_Check (obj))
+    str = pytave::extract_py_str (obj);
+  else if (Py_TYPE (obj)->tp_str != NULL)
+    {
+      PyObject *s = PyObject_Str (obj);
+      str = pytave::extract_py_str (s);
+      Py_DECREF (s);
+    }
+  else
+    {
+      Py_DECREF (obj);
+      error ("pyobject.char: cannot convert Python object to string");
+    }
+
+  Py_DECREF (obj);
+
+  return ovl (str);
+}
+
 DEFUN_DLD (__py_struct_from_dict__, args, nargout,
            "-*- texinfo -*-\n\
 @deftypefn  {} {} __py_struct_from_dict__ (@var{dict})\n\
--- a/pyargs.m	Thu Aug 25 12:11:02 2016 -0700
+++ b/pyargs.m	Thu Aug 25 14:06:56 2016 -0700
@@ -32,7 +32,7 @@
 ## py.dict (pyargs ("one", 1, "two", 2))
 ##       @result{} [pyobject ...]
 ##           @{...@}
-## sort (cell (py.list (ans.keys ())))
+## sort (cellfun (@@char, cell (py.list (ans.keys ())), "uniformoutput", false))
 ##       @result{}
 ##           @{
 ##             [1,1] = one
@@ -90,9 +90,9 @@
 
 
 %!assert (isa (pyargs (), "pyobject"))
-%!assert (sort (cell (py.list (py.dict (pyargs ()).keys ()))), cell (1, 0))
-%!xtest assert (sort (cell (py.list (py.dict (pyargs ("one", 1)).keys ()))), {"one"})
-%!assert (sort (cell (py.list (py.dict (pyargs ("one", 1, "two", 2)).keys ()))), {"one", "two"})
+%!assert (cell (py.list (py.dict (pyargs ()).keys ())), cell (1, 0))
+%!assert (sort (cellfun (@char, cell (py.list (py.dict (pyargs ("one", 1)).keys ())), "uniformoutput", false)), {"one"})
+%!assert (sort (cellfun (@char, cell (py.list (py.dict (pyargs ("one", 1, "two", 2)).keys ())), "uniformoutput", false)), {"one", "two"})
 
 %!error pyargs (1)
 %!error pyargs (1, 2)
--- a/pycall.cc	Thu Aug 25 12:11:02 2016 -0700
+++ b/pycall.cc	Thu Aug 25 14:06:56 2016 -0700
@@ -147,13 +147,13 @@
 }
 
 /*
-%!assert (ischar (pycall ("os.getcwd")))
 %!assert (isreal (pycall ("random.random")))
 %!assert (double (pycall ("math.exp", 3)), exp (3))
 %!assert (double (pycall ("math.trunc", pi)), fix (pi))
 %!assert (double (pycall ("math.sqrt", 2)), sqrt (2))
 %!assert (double (pycall ("cmath.sqrt", 2j)), sqrt (2j))
 %!assert (double (pycall ("int", 10.2)), 10)
+%!assert (isa (pycall ("os.getcwd"), "pyobject"))
 %!assert (isa (pycall ("object"), "pyobject"))
 %!assert (isa (pycall ("dict"), "pyobject"))
 %!assert (isa (pycall ("list"), "pyobject"))
@@ -166,14 +166,14 @@
 %!          "    if s == 'long':\n" ...
 %!          "        return 'int'\n" ...
 %!          "    return s"]);
-%!assert (pycall ("typename", 0), "float")
-%!assert (pycall ("typename", pi), "float")
-%!assert (pycall ("typename", 2j), "complex")
-%!assert (pycall ("typename", int32 (0)), "int")
-%!assert (pycall ("typename", false), "bool")
-%!assert (pycall ("typename", true), "bool")
-%!assert (pycall ("typename", "Hello world"), "str")
-%!assert (pycall ("typename", char ([1, 2, 3])), "str")
+%!assert (char (pycall ("typename", 0)), "float")
+%!assert (char (pycall ("typename", pi)), "float")
+%!assert (char (pycall ("typename", 2j)), "complex")
+%!assert (char (pycall ("typename", int32 (0))), "int")
+%!assert (char (pycall ("typename", false)), "bool")
+%!assert (char (pycall ("typename", true)), "bool")
+%!assert (char (pycall ("typename", "Hello world")), "str")
+%!assert (char (pycall ("typename", char ([1, 2, 3]))), "str")
 
 ## Test construction of sequence types from cell arrays
 %!assert (char (pycall ("list")), "[]")
@@ -190,7 +190,7 @@
 ## Test construction of dict from pyargs
 %!test
 %! a = pycall ("dict", pyargs ("a", 1, "b", 2, "c", 3));
-%! assert (sort (cell (pycall ("list", a.keys ()))), {"a", "b", "c"})
+%! assert (sort (cellfun (@char, cell (pycall ("list", a.keys ())), "uniformoutput", false)), {"a", "b", "c"})
 %! assert (sort (double (pycall ("array.array", "d", a.values ()))), [1, 2, 3])
 
 ## Test copy construction of dict from dict
@@ -208,7 +208,7 @@
 ## Test round trip type preservation / conversion
 %!test
 %! pyexec ("def roundtrip(x): return x");
-%! values = { 0, pi, 2j, eps, false, true, version, "Hello world" };
+%! values = { 0, pi, 2j, eps, false, true };
 %! for i = 1:numel (values)
 %!   assert (pycall ("roundtrip", values{i}), values{i});
 %! endfor
--- a/pyeval.cc	Thu Aug 25 12:11:02 2016 -0700
+++ b/pyeval.cc	Thu Aug 25 14:06:56 2016 -0700
@@ -125,8 +125,8 @@
 %!assert (iscomplex (pyeval ("2j")))
 %!assert (pyeval ("2j"), 2j)
 
-%!assert (ischar (pyeval ("\"I <3 Octave\"")))
-%!assert (pyeval ("\"I <3 Octave\""), "I <3 Octave")
+%!assert (ischar (char (pyeval ("\"I <3 Octave\""))))
+%!assert (char (pyeval ("\"I <3 Octave\"")), "I <3 Octave")
 
 %!assert (islogical (pyeval ("True")))
 %!assert (islogical (pyeval ("False")))
--- a/python_to_octave.cc	Thu Aug 25 12:11:02 2016 -0700
+++ b/python_to_octave.cc	Thu Aug 25 14:06:56 2016 -0700
@@ -327,8 +327,6 @@
       oct_value = extract_py_complex (py_object.ptr ());
     else if (arrayx.check ())
       pyarr_to_octvalue (oct_value, (PyArrayObject*)py_object.ptr ());
-    else if (PyBytes_Check (py_object.ptr ()) || PyUnicode_Check (py_object.ptr ()))
-      oct_value = extract_py_str (py_object.ptr ());
     else
       oct_value = pyobject_wrap_object (py_object.ptr ());
   }