changeset 307:b4e56f7255f7

Add option to specify namespace when calling pyexec and pyeval (fixes issue #25) * pyeval.cc, pyexec.cc: Use the second parameter (if supplied) as namespace to eval/exec the python code.
author Abhinav Tripathi <genuinelucifer@gmail.com>
date Tue, 09 Aug 2016 11:28:15 -0700
parents 2ecae5c6eeb6
children 3e0decdf59b0
files pyeval.cc pyexec.cc
diffstat 2 files changed, 80 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/pyeval.cc	Tue Aug 09 11:27:38 2016 -0700
+++ b/pyeval.cc	Tue Aug 09 11:28:15 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 <NameError>
 %! 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 <NameError>
+%! 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 <NameError>
+%! 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 <NameError>
+%! 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");
 */
--- a/pyexec.cc	Tue Aug 09 11:27:38 2016 -0700
+++ b/pyexec.cc	Tue Aug 09 11:28:15 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 &)
     {