# HG changeset patch # User Mike Miller # Date 1472143918 25200 # Node ID b0677c492655ff10155e8ba812f1aacd0bd0444a # Parent 07c1b457cb6b3af44c364538d46830b1e068d506 Overhaul Python object storage and wrapping in pyobject * oct-py-util.cc, oct-py-util.h (pytave::py_objstore_del, pytave::py_objstore_get, pytave::py_objstore_put, pytave::pyobject_wrap_object, pytave::pyobject_unwrap_object): New functions. (pytave::get_object_from_python): Use pyobject_unwrap_object. * octave_to_python.cc (pytave::octvalue_to_pyobj): Use pyobject_unwrap_object. * python_to_octave.cc (pytave::pyobj_to_octvalue): Use pyobject_wrap_object. (pytave::pyobj_to_oct_pyobject): Delete. * __py_struct_from_dict__.cc (F__py_objstore_del__, F__py_objstore_get__, F__py_objstore_put__): New functions. * @pyobject/pyobject.m (pyobject.m_id): Rename from id. (pyobject.pyobject): Use __py_objstore_put__, simplify conditional logic. (pyobject.delete): Use __py_objstore_del__. (pyobject.id): Rename from getid. * @pyobject/display.m: Use pyobject.id method name, format as hex. diff -r 07c1b457cb6b -r b0677c492655 @pyobject/display.m --- a/@pyobject/display.m Thu Aug 25 09:29:51 2016 -0700 +++ b/@pyobject/display.m Thu Aug 25 09:51:58 2016 -0700 @@ -41,7 +41,7 @@ loose = ! __compactformat__ (); - printf ("%s = [pyobject %s]\n", inputname (1), getid (x)); + printf ("%s = [pyobject 0x%x]\n", inputname (1), id (x)); s = char (x); s = make_indented (s); if (loose), printf ("\n"); endif diff -r 07c1b457cb6b -r b0677c492655 @pyobject/pyobject.m --- a/@pyobject/pyobject.m Thu Aug 25 09:29:51 2016 -0700 +++ b/@pyobject/pyobject.m Thu Aug 25 09:51:58 2016 -0700 @@ -29,7 +29,7 @@ classdef pyobject < handle properties - id + m_id endproperties @@ -37,42 +37,22 @@ function obj = pyobject (x, id) if (nargin == 0) obj = pyeval ("None"); - return - endif - - if (nargin == 1) + elseif (nargin == 1) ## Convert the input to a pyobject if (isa (x, "pyobject")) obj = x; else - ## Ensure dict for Octave communication exists - cmd = [ "if not getattr(__import__('__main__'), '_in_octave', None):\n" ... - " __import__('__main__')._in_octave = dict()" ]; - pyexec (cmd); - - ## Function to insert and return the hex id - cmd = [ "def _in_octave_insert(x):\n" ... - " h = hex(id(x))\n" ... - " __import__('__main__')._in_octave[h] = x\n" ... - " return h" ]; - pyexec (cmd); - - id = pycall ("_in_octave_insert", x); - obj = pyobject (0, id); + obj.m_id = __py_objstore_put__ (x); endif - return - endif - - if (nargin == 2) + elseif (nargin == 2) ## The actual constructor. Nicer to split this off to static method ## like `pyobject.new` but I don't know how to call from pycall.cc. ## Warning: not intended for casual use: you must also insert the - ## object into the Python `_in_octave` dict with key `id`. - obj.id = id; - return + ## object into the Python object store with key `id`. + obj.m_id = id; + else + error ("pyobject: unexpected input to the constructor") endif - - error ("pyobject: unexpected input to the constructor") endfunction function delete (x) @@ -93,9 +73,7 @@ #disp ("delete") - ## throws KeyError if it wasn't in there for some reason - cmd = sprintf ("__import__('__main__')._in_octave.pop('%s')", x.id); - pyexec (cmd) + __py_objstore_del__ (x.m_id); endfunction # methods defined in external files @@ -103,8 +81,8 @@ display (x) subsref (x, idx) - function r = getid (x) - r = x.id; + function r = id (x); + r = x.m_id; endfunction function s = char (x) diff -r 07c1b457cb6b -r b0677c492655 __py_struct_from_dict__.cc --- a/__py_struct_from_dict__.cc Thu Aug 25 09:29:51 2016 -0700 +++ b/__py_struct_from_dict__.cc Thu Aug 25 09:51:58 2016 -0700 @@ -134,6 +134,83 @@ return retval; } +DEFUN_DLD (__py_objstore_del__, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {} {} __py_objstore_del__ (@var{key})\n\ +Delete the Python object stored under @var{key} from the object store.\n\ +\n\ +This is a private internal function not intended for direct use.\n\ +@end deftypefn") +{ + if (args.length () != 1) + print_usage (); + + Py_Initialize (); + uint64_t key = args(0).xuint64_scalar_value ("__py_objstore_del__: KEY must be an integer"); + pytave::py_objstore_del (key); + + return ovl (); +} + +DEFUN_DLD (__py_objstore_get__, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {} {} __py_objstore_get__ (@var{key})\n\ +Get the Python object stored under @var{key} from the object store.\n\ +\n\ +This is a private internal function not intended for direct use.\n\ +@end deftypefn") +{ + if (args.length () != 1) + print_usage (); + + Py_Initialize (); + uint64_t key = args(0).xuint64_scalar_value ("__py_objstore_get__: KEY must be an integer"); + PyObject *obj = pytave::py_objstore_get (key); + + if (! obj) + error ("__py_objstore_get__: no existing Python object found for key %ju", key); + + octave_value retval = pytave::pyobject_wrap_object (obj); + + return ovl (retval); +} + +DEFUN_DLD (__py_objstore_put__, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {} {} __py_objstore_put__ (@var{value})\n\ +Convert @var{value} to a Python value and store in the object store.\n\ +\n\ +This is a private internal function not intended for direct use.\n\ +@end deftypefn") +{ + if (args.length () != 1) + print_usage (); + + Py_Initialize (); + boost::python::numeric::array::set_module_and_type ("numpy", "ndarray"); + _import_array (); + // FIXME: PyObject *obj = convert argument to Python (args(0)); + PyObject *obj = 0; + try + { + boost::python::object arg; + pytave::octvalue_to_pyobj (arg, args(0)); + obj = arg.ptr (); + Py_INCREF (obj); + } + catch (pytave::value_convert_exception const &) + { + } + + if (! obj) + error ("__py_objstore_put__: VALUE must be convertible to a Python value"); + + uint64_t key = pytave::py_objstore_put (obj); + Py_DECREF (obj); + + return ovl (octave_uint64 (key)); +} + DEFUN_DLD (__py_struct_from_dict__, args, nargout, "-*- texinfo -*-\n\ @deftypefn {} {} __py_struct_from_dict__ (@var{dict})\n\ diff -r 07c1b457cb6b -r b0677c492655 oct-py-util.cc --- a/oct-py-util.cc Thu Aug 25 09:29:51 2016 -0700 +++ b/oct-py-util.cc Thu Aug 25 09:51:58 2016 -0700 @@ -51,10 +51,8 @@ { if (oct_value.is_object () && oct_value.class_name () == "pyobject") { - octave_value_list tmp = feval ("getid", ovl (oct_value), 1); - std::string hexid = tmp(0).string_value (); - object main_module = import ("__main__"); - py_object = main_module.attr ("_in_octave")[hexid]; + PyObject *obj = pyobject_unwrap_object (oct_value); + py_object = boost::python::object (boost::python::handle<> (obj)); } else py_object = boost::python::object (); // None @@ -149,6 +147,95 @@ return name_ ? extract_py_str (name_): ""; } +// FIXME: could make this into a class/singleton wrapper a la Octave core +PyObject *objstore = 0; + +inline PyObject * +py_objstore () +{ + if (! objstore) + { + PyObject *main = py_import_module ("__main__"); + PyObject *ns = main ? PyObject_GetAttrString (main, "__dict__") : 0; + PyObject *dict = ns ? PyDict_GetItemString (ns, "_in_octave") : 0; + + if (dict) + Py_INCREF (dict); + + if (! dict) + { + dict = PyDict_New (); + if (dict && ns) + PyDict_SetItemString (ns, "_in_octave", dict); + } + + if (! dict) + throw boost::python::error_already_set (); + + objstore = dict; + } + return objstore; +} + +void +py_objstore_del (uint64_t key) +{ + PyObject *store = py_objstore (); + PyObject *key_obj = make_py_int (key); + PyObject *key_fmt = PyNumber_ToBase (key_obj, 16); + PyDict_DelItem (store, key_fmt); + Py_DECREF (key_fmt); + Py_DECREF (key_obj); +} + +PyObject * +py_objstore_get (uint64_t key) +{ + PyObject *store = py_objstore (); + PyObject *key_obj = make_py_int (key); + PyObject *key_fmt = PyNumber_ToBase (key_obj, 16); + PyObject *obj = PyDict_GetItem (store, key_fmt); + Py_DECREF (key_fmt); + Py_DECREF (key_obj); + if (obj) + Py_INCREF (obj); + return obj; +} + +uint64_t +py_objstore_put (PyObject *obj) +{ + PyObject *store = py_objstore (); + uint64_t key = reinterpret_cast (obj); + PyObject *key_obj = make_py_int (key); + PyObject *key_fmt = PyNumber_ToBase (key_obj, 16); + PyDict_SetItem (store, key_fmt, obj); + Py_DECREF (key_fmt); + Py_DECREF (key_obj); + return key; +} + +octave_value +pyobject_wrap_object (PyObject *obj) +{ + uint64_t key = py_objstore_put (obj); + octave_value_list out = feval ("pyobject", ovl (0, octave_uint64 (key)), 1); + return out(0); +} + +PyObject * +pyobject_unwrap_object (const octave_value& value) +{ + if (value.is_object () && value.class_name () == "pyobject") + { + octave_value_list out = feval ("id", ovl (value), 1); + uint64_t key = out(0).uint64_scalar_value (); + return py_objstore_get (key); + } + + return 0; +} + bool is_py_kwargs_argument (PyObject *obj) { diff -r 07c1b457cb6b -r b0677c492655 oct-py-util.h --- a/oct-py-util.h Thu Aug 25 09:29:51 2016 -0700 +++ b/oct-py-util.h Thu Aug 25 09:51:58 2016 -0700 @@ -92,6 +92,21 @@ std::string py_object_class_name (PyObject *obj); +void +py_objstore_del (uint64_t key); + +PyObject * +py_objstore_get (uint64_t key); + +uint64_t +py_objstore_put (PyObject *obj); + +octave_value +pyobject_wrap_object (PyObject *obj); + +PyObject * +pyobject_unwrap_object (const octave_value& value); + bool is_py_kwargs_argument (PyObject *obj); diff -r 07c1b457cb6b -r b0677c492655 octave_to_python.cc --- a/octave_to_python.cc Thu Aug 25 09:29:51 2016 -0700 +++ b/octave_to_python.cc Thu Aug 25 09:51:58 2016 -0700 @@ -38,6 +38,7 @@ #include "exceptions.h" #include "octave_to_python.h" #include "oct-py-types.h" +#include "oct-py-util.h" using namespace boost::python; @@ -194,9 +195,8 @@ } else if (octvalue.is_object () && octvalue.class_name () == "pyobject") { - octave_value_list tmp = feval ("getid", ovl (octvalue), 1); - std::string hexid = tmp(0).string_value (); - py_object = boost::python::import ("__main__").attr ("_in_octave")[hexid]; + PyObject *obj = pyobject_unwrap_object (octvalue); + py_object = object (handle (obj)); } else throw value_convert_exception ( diff -r 07c1b457cb6b -r b0677c492655 python_to_octave.cc --- a/python_to_octave.cc Thu Aug 25 09:29:51 2016 -0700 +++ b/python_to_octave.cc Thu Aug 25 09:51:58 2016 -0700 @@ -310,27 +310,6 @@ } } - static void - pyobj_to_oct_pyobject (octave_value& oct_value, - const boost::python::object& py_object) - { - object main_module = import ("__main__"); - object builtins_module; - pytave::get_builtins_module (builtins_module); - object hex_function = builtins_module.attr ("hex"); - object id_function = builtins_module.attr ("id"); - object idtmp = hex_function (id_function (py_object)); - std::string id = extract (idtmp); - - // Ensure dict for Octave communication exists - if (! PyObject_HasAttrString (main_module.ptr (), "_in_octave")) - main_module.attr ("_in_octave") = boost::python::dict (); - - main_module.attr ("_in_octave")[id] = py_object; - // Create @pyobject - oct_value = feval ("pyobject", ovl (0, id), 1)(0); - } - void pyobj_to_octvalue (octave_value& oct_value, const boost::python::object& py_object) { @@ -351,7 +330,7 @@ else if (PyBytes_Check (py_object.ptr ()) || PyUnicode_Check (py_object.ptr ())) oct_value = extract_py_str (py_object.ptr ()); else - pyobj_to_oct_pyobject (oct_value, py_object); + oct_value = pyobject_wrap_object (py_object.ptr ()); } void pytuple_to_octlist (octave_value_list& octave_list,