# HG changeset patch # User Mike Miller # Date 1470781537 25200 # Node ID 3e0decdf59b0ea30446a946d8b90a7321fdeb387 # Parent 616ec5f18d957efa1920f22425991ed4e6d3ac9e# Parent b4e56f7255f7446e5b6088d0543f4812aa9c5016 Merged in genuinelucifer/pytave_main (pull request #24) Refactor code and add option to specify namespace for pyexec and pyeval (fixes issue #25) diff -r 616ec5f18d95 -r 3e0decdf59b0 Makefile.am --- a/Makefile.am Mon Aug 08 17:34:09 2016 -0700 +++ b/Makefile.am Tue Aug 09 15:25:37 2016 -0700 @@ -27,7 +27,8 @@ COMMON_SOURCE_FILES = \ exceptions.cc \ octave_to_python.cc \ - python_to_octave.cc + python_to_octave.cc \ + pytave_utils.cc DOC_FILES = \ INSTALL.md \ @@ -45,7 +46,8 @@ config.h \ exceptions.h \ octave_to_python.h \ - python_to_octave.h + python_to_octave.h \ + pytave_utils.h PY_FILES = \ package/__init__.py \ diff -r 616ec5f18d95 -r 3e0decdf59b0 pycall.cc --- a/pycall.cc Mon Aug 08 17:34:09 2016 -0700 +++ b/pycall.cc Tue Aug 09 15:25:37 2016 -0700 @@ -35,6 +35,7 @@ #include "exceptions.h" #include "octave_to_python.h" #include "python_to_octave.h" +#include "pytave_utils.h" using namespace boost::python; @@ -87,64 +88,18 @@ return retval; } - bool func_by_name = false; - - if (args(0).is_string ()) - func_by_name = true; - else if (args(0).is_object () && args(0).class_name () == "pyobject") - func_by_name = false; - else - error ("pycall: FUNC must be a string or a Python reference"); - Py_Initialize (); pytave::init_exceptions (); numeric::array::set_module_and_type ("numpy", "ndarray"); _import_array (); - object main_module = import ("__main__"); - object main_namespace = main_module.attr ("__dict__"); -#if PY_VERSION_HEX >= 0x03000000 - object builtins_module = import ("builtins"); -#else - object builtins_module = import ("__builtin__"); -#endif - try { object callable; - - if (func_by_name) - { - std::string module; - std::string func = args(0).string_value (); - - size_t idx = func.rfind ("."); - if (idx != std::string::npos) - { - module = func.substr (0, idx); - func = func.substr (idx + 1); - } - - object mod; - if (module.empty ()) - { - if (PyObject_HasAttrString (main_module.ptr (), func.c_str ())) - mod = main_module; - else - mod = builtins_module; - } - else - mod = import (module.c_str ()); - - callable = mod.attr (func.c_str ()); - } - else - { - octave_value_list tmp = feval ("getid", ovl (args(0)), 1); - std::string hexid = tmp(0).string_value (); - callable = main_module.attr ("__InOct__")[hexid]; - } + pytave::get_object_from_python (args(0), callable); + if (callable.is_none ()) + error("pycall: FUNC must be a string or a Python reference"); PyObject *pyargs = PyTuple_New (nargin - 1); for (int i = 1; i < nargin; i++) diff -r 616ec5f18d95 -r 3e0decdf59b0 pyeval.cc --- a/pyeval.cc Mon Aug 08 17:34:09 2016 -0700 +++ b/pyeval.cc Tue Aug 09 15:25:37 2016 -0700 @@ -35,6 +35,7 @@ #include "arrayobjectdefs.h" #include "exceptions.h" #include "python_to_octave.h" +#include "pytave_utils.h" using namespace boost::python; @@ -42,7 +43,10 @@ "-*- texinfo -*-\n\ @deftypefn {} {} pyeval (@var{expr})\n\ @deftypefnx {} {@var{x} =} pyeval (@var{expr})\n\ +@deftypefn {} {} pyeval (@var{expr}, @var{localNS})\n\ +@deftypefnx {} {@var{x} =} pyeval (@var{expr}, @var{localNS})\n\ Evaluate a Python expression and return the result.\n\ +You can supply a 'localNS' to enforce all changes in that namespace.\n\ \n\ Examples:\n\ @example\n\ @@ -63,6 +67,12 @@ int nargin = args.length (); + if (nargin < 1 || nargin > 2) + { + print_usage (); + return retval; + } + std::string code = args(0).string_value (); std::string id; @@ -72,10 +82,19 @@ object main_module = import ("__main__"); object main_namespace = main_module.attr ("__dict__"); + object local_namespace; + if (nargin > 1) + { + pytave::get_object_from_python (args(1), local_namespace); + if (local_namespace.is_none ()) + error ("pyeval: NAMESPACE must be a string or a Python reference"); + } + else + local_namespace = main_namespace; try { - res = eval (code.c_str (), main_namespace, main_namespace); + res = eval (code.c_str (), main_namespace, local_namespace); if (nargout > 0 || ! res.is_none ()) { @@ -154,4 +173,50 @@ %!error %! pyexec ("def raiseException(): raise NameError ('oops')") %! pyeval ("raiseException()") + +%!test +%! % Variable defined in global namespace is available locally +%! myNS = pyeval ("{}"); +%! pyexec ("myvar = 1") +%! assert (pyeval ("myvar", myNS), 1); + +%!test +%! % Variables with same name can have different values in different namespaces +%! myNS1 = pyeval ("{}"); +%! myNS2 = pyeval ("{}"); +%! pyexec ("myvar = 1") +%! pyexec ("myvar = 2", myNS1) +%! pyexec ("myvar = 3", myNS2) +%! assert (pyeval ("myvar"), 1) +%! assert (pyeval ("myvar", myNS1), 2) +%! assert (pyeval ("myvar", myNS2), 3) + +%!test +%! pyexec ("if 'myvar' in globals(): del myvar") +%! % Namespaces can also be passed as strings +%! pyexec ("myNS = {}"); +%! pyexec ("myvar = 1", "myNS"); +%! assert (pyeval ("myvar", "myNS"), 1); + +%!error +%! pyexec ("if 'myvar' in globals(): del myvar") +%! % Variable defined in local namespace MUST not be available globally +%! myNS = pyeval ("{}"); +%! pyexec ("myvar = 1", myNS) +%! pyeval ("myvar"); + +%!error +%! pyexec ("if 'myvar' in globals(): del myvar") +%! % Variable defined in one local namespace MUST not be available in another +%! myNS1 = pyeval ("{}"); +%! myNS2 = pyeval ("{}"); +%! pyexec ("myvar = 1", myNS1) +%! pyeval ("myvar", myNS2); + +%!error +%! pyexec ("if 'sys' in globals(): del sys") +%! % Modules imported in local namespace MUST not be accessible globally +%! myNS = pyeval ("{}"); +%! pyexec ("import sys", myNS); +%! pyeval ("sys"); */ diff -r 616ec5f18d95 -r 3e0decdf59b0 pyexec.cc --- a/pyexec.cc Mon Aug 08 17:34:09 2016 -0700 +++ b/pyexec.cc Tue Aug 09 15:25:37 2016 -0700 @@ -34,13 +34,16 @@ #include "arrayobjectdefs.h" #include "exceptions.h" #include "python_to_octave.h" +#include "pytave_utils.h" using namespace boost::python; DEFUN_DLD (pyexec, args, nargout, "-*- texinfo -*-\n\ @deftypefn {} {} pyexec (@var{expr})\n\ +@deftypefn {} {} pyexec (@var{expr}, @var{localNS})\n\ Execute a Python expression or block of code.\n\ +You can supply a 'localNS' to enforce all changes in that namespace.\n\ \n\ Examples:\n\ @example\n\ @@ -56,7 +59,7 @@ int nargin = args.length (); - if (nargin != 1) + if (nargin < 1 || nargin > 2) { print_usage (); return retval; @@ -68,12 +71,21 @@ object main_module = import ("__main__"); object main_namespace = main_module.attr ("__dict__"); + object local_namespace; + if (nargin > 1) + { + pytave::get_object_from_python (args(1), local_namespace); + if (local_namespace.is_none ()) + error ("pyexec: NAMESPACE must be a string or a Python reference"); + } + else + local_namespace = main_namespace; try { // FIXME: figure out exec return code: // http://www.boost.org/doc/libs/1_38_0/libs/python/doc/v2/exec.html - exec (code.c_str (), main_namespace, main_namespace); + exec (code.c_str (), main_namespace, local_namespace); } catch (pytave::object_convert_exception const &) { diff -r 616ec5f18d95 -r 3e0decdf59b0 pytave_utils.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytave_utils.cc Tue Aug 09 15:25:37 2016 -0700 @@ -0,0 +1,94 @@ +/* + +Copyright (C) 2016 Abhinav Tripathi + +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 +. + +*/ + +#if defined (HAVE_CONFIG_H) +# include +#endif + +#include +#include + +#include "pytave_utils.h" + +using namespace boost::python; + +namespace pytave +{ + void get_builtins_module (boost::python::object& builtins_module) + { +#if PY_VERSION_HEX >= 0x03000000 + builtins_module = import ("builtins"); +#else + builtins_module = import ("__builtin__"); +#endif + } + + void get_object_from_python (const octave_value& oct_value, + boost::python::object& py_object) + { + bool by_name; + if (oct_value.is_string ()) + by_name = true; + else if (oct_value.is_object () && oct_value.class_name () == "pyobject") + by_name = false; + else + { + py_object = boost::python::object(); //None + return; + } + + object main_module = import ("__main__"); + object builtins_module; + get_builtins_module (builtins_module); + + if (by_name) + { + std::string module; + std::string func = oct_value.string_value (); + + size_t idx = func.rfind ("."); + if (idx != std::string::npos) + { + module = func.substr (0, idx); + func = func.substr (idx + 1); + } + + object mod; + if (module.empty ()) + { + if (PyObject_HasAttrString (main_module.ptr (), func.c_str ())) + mod = main_module; + else + mod = builtins_module; + } + else + mod = import (module.c_str ()); + py_object = mod.attr (func.c_str ()); + } + else + { + octave_value_list tmp = feval ("getid", ovl (oct_value), 1); + std::string hexid = tmp(0).string_value (); + py_object = main_module.attr ("__InOct__")[hexid]; + } + } +} diff -r 616ec5f18d95 -r 3e0decdf59b0 pytave_utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytave_utils.h Tue Aug 09 15:25:37 2016 -0700 @@ -0,0 +1,35 @@ +/* + +Copyright (C) 2016 Abhinav Tripathi + +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 +. + +*/ + +#if ! defined (pytave_utils_h) +#define pytave_utils_h + +#include + +namespace pytave +{ + void get_builtins_module (boost::python::object& builtins_module); + void get_object_from_python (const octave_value& oct_value, + boost::python::object& py_object); +} + +#endif diff -r 616ec5f18d95 -r 3e0decdf59b0 python_to_octave.cc --- a/python_to_octave.cc Mon Aug 08 17:34:09 2016 -0700 +++ b/python_to_octave.cc Tue Aug 09 15:25:37 2016 -0700 @@ -41,6 +41,7 @@ #include "arrayobjectdefs.h" #include "exceptions.h" #include "python_to_octave.h" +#include "pytave_utils.h" using namespace boost::python; @@ -481,11 +482,8 @@ { object main_module = import ("__main__"); object main_namespace = main_module.attr ("__dict__"); -#if PY_VERSION_HEX >= 0x03000000 - object builtins_module = import ("builtins"); -#else - object builtins_module = import ("__builtin__"); -#endif + 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));