changeset 373:0e4097c66788

Report a Python object's fully qualified class name correctly * oct-py-util.cc (pytave::py_object_class_name): Return the correct fully qualified class name, including module prefix. (pytave::is_py_kwargs_argument): Check for class name "__main__._OctaveKwargs". * __py_struct_from_dict__.cc (F__py_class_name__): New function. * @pyobject/methods.m (pyobject.methods): Use __py_class_name__. * @pyobject/pyobject.m (pyobject.class): Likewise.
author Mike Miller <mtmiller@octave.org>
date Fri, 26 Aug 2016 14:05:37 -0700
parents b20b8cf8ad07
children d362cdd1ddeb
files @pyobject/methods.m @pyobject/pyobject.m __py_struct_from_dict__.cc oct-py-util.cc
diffstat 4 files changed, 66 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/@pyobject/methods.m	Fri Aug 26 13:59:18 2016 -0700
+++ b/@pyobject/methods.m	Fri Aug 26 14:05:37 2016 -0700
@@ -79,10 +79,7 @@
       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 = char (pycall ("getattr", classref, "__name__"));
-      printf ("Methods for Python class '%s':\n", classname);
+      printf ("Methods for Python class '%s':\n", __py_class_name__ (x));
     endif
     disp (list_in_columns (mtds_list));
   else
--- a/@pyobject/pyobject.m	Fri Aug 26 13:59:18 2016 -0700
+++ b/@pyobject/pyobject.m	Fri Aug 26 14:05:37 2016 -0700
@@ -96,11 +96,8 @@
     endfunction
 
     function s = class (x)
-      idx = struct ("type", ".", "subs", "__class__");
-      class_ref = subsref (x, idx);
-      idx = struct ("type", ".", "subs", "__name__");
-      s = subsref (class_ref, idx);
-      s = sprintf ("py.%s", char (s));
+      s = __py_class_name__ (x);
+      s = sprintf ("py.%s", s);
     endfunction
 
     function y = double (x)
--- a/__py_struct_from_dict__.cc	Fri Aug 26 13:59:18 2016 -0700
+++ b/__py_struct_from_dict__.cc	Fri Aug 26 14:05:37 2016 -0700
@@ -34,6 +34,41 @@
 #include "oct-py-util.h"
 #include "octave_to_python.h"
 
+DEFUN_DLD (__py_class_name__, args, ,
+           "-*- texinfo -*-\n\
+@deftypefn  {} {} __py_class_name__ (@var{obj})\n\
+Return the name of the class 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 ("__py_class_name__: argument must be a valid Python object");
+
+  Py_Initialize ();
+
+  PyObject *obj = pytave::pyobject_unwrap_object (args(0));
+  std::string name = pytave::py_object_class_name (obj);
+  Py_DECREF (obj);
+
+  return ovl (name);
+}
+
+/*
+%!assert (__py_class_name__ (pyeval ("None")), "NoneType")
+%!assert (__py_class_name__ (pyeval ("0")), "int")
+%!assert (__py_class_name__ (pyeval ("'Octave'")), "str")
+%!assert (__py_class_name__ (pyeval ("[]")), "list")
+%!assert (__py_class_name__ (pyeval ("__import__('array').array('d')")), "array.array")
+
+%!error __py_class_name__ ()
+%!error __py_class_name__ (1)
+%!error __py_class_name__ (1, 2)
+*/
+
 DEFUN_DLD (__py_int64_scalar_value__, args, nargout,
            "-*- texinfo -*-\n\
 @deftypefn  {} {} __py_int64_scalar_value__ (@var{x})\n\
--- a/oct-py-util.cc	Fri Aug 26 13:59:18 2016 -0700
+++ b/oct-py-util.cc	Fri Aug 26 14:05:37 2016 -0700
@@ -126,9 +126,33 @@
 std::string
 py_object_class_name (PyObject *obj)
 {
-  PyObject *class_ = obj ? PyObject_GetAttrString (obj, "__class__") : 0;
-  PyObject *name_ = class_ ? PyObject_GetAttrString (class_, "__name__") : 0;
-  return name_ ? extract_py_str (name_): "";
+  std::string retval;
+
+  PyObject *type = obj ? PyObject_GetAttrString (obj, "__class__") : 0;
+  if (type)
+    {
+      PyObject *mod = PyObject_GetAttrString (type, "__module__");
+
+      PyObject *name = 0;
+      if (PyObject_HasAttrString (type, "__qualname__"))
+        name = PyObject_GetAttrString (type, "__qualname__");
+      else
+        name = PyObject_GetAttrString (type, "__name__");
+
+      std::string mod_str = mod ? extract_py_str (mod) : "";
+      std::string name_str = name ? extract_py_str (name) : "";
+
+      Py_DECREF (type);
+      Py_XDECREF (mod);
+      Py_XDECREF (name);
+
+      if (mod_str == py_builtins_module_name ())
+        retval = name_str;
+      else
+        retval = mod_str + "." + name_str;
+    }
+
+  return retval;
 }
 
 // FIXME: could make this into a class/singleton wrapper a la Octave core
@@ -223,7 +247,7 @@
 bool
 is_py_kwargs_argument (PyObject *obj)
 {
-  if (obj && py_object_class_name (obj) == "_OctaveKwargs"
+  if (obj && py_object_class_name (obj) == "__main__._OctaveKwargs"
       && PyObject_HasAttrString (obj, "is_kwargs_argument"))
     {
       PyObject *flag = PyObject_GetAttrString (obj, "is_kwargs_argument");