Commits

ariovistus committed d1e7750

added InterpContext

  • Participants
  • Parent commits c3f86c2

Comments (0)

Files changed (6)

File examples/pyd_unittests/embedded.d

 import pyd.pyd, pyd.embedded;
 
+import std.stdio;
+
 static this() {
     on_py_init({
     add_module!(ModuleName!"testing")();
     assert(func1(3) == 7);    
 }
 
+// py_stmts
+unittest {
+    // futures do not persist across py_stmts calls
+    py_stmts(
+            "a = 3 / 4;"
+            ,
+            "testing");
+    assert(py_eval!double("a", "testing") == 0);
+    py_stmts(
+            "from __future__ import division\n"
+            "b = 3 / 4;"
+            ,
+            "testing");
+    assert(py_eval!double("b", "testing") == 0.75);
+    py_stmts(
+            "a = 3 / 4;"
+            ,
+            "testing");
+    assert(py_eval!double("a", "testing") == 0);
+
+    // but they do across contextual py_stmts calls.
+    InterpContext c = new InterpContext();
+    c.py_stmts(
+            "import testing\n"
+            "a = 3 / 4;"
+            );
+    assert(c.py_eval!double("a") == 0);
+    c.py_stmts(
+            "from __future__ import division\n"
+            "b = 3 / 4;"
+            );
+    assert(c.py_eval!double("b") == 0.75);
+    c.py_stmts(
+            "a = 3 / 4;"
+            );
+    assert(c.py_eval!double("a") == 0.75);
+}
+unittest {
+    // py_stmts with modulename executes within that module
+
+    py_stmts(
+            "a = \"doctor!\""
+            ,
+            "testing");
+    py_stmts(
+            "import testing\n"
+            "assert testing.a == \"doctor!\""
+            );
+
+    // however, py_stmts contextualized or without modulename does not.
+
+    py_stmts(
+            "import testing\n"
+            "a = \"nurse!\""
+            );
+    py_stmts(
+            "import testing\n"
+            "assert testing.a == \"doctor!\""
+            );
+    InterpContext c = new InterpContext();
+    c.py_stmts(
+            "import testing\n"
+            "a = \"nurse!\""
+            );
+    py_stmts(
+            "import testing\n"
+            "assert testing.a == \"doctor!\""
+            );
+
+}
+
 void main() {}

File examples/pyd_unittests/embedded3.d

     assert(func1(3) == 7);    
 }
 
+unittest {
+    // py_stmts with modulename executes within that module
+
+    py_stmts(
+            "a = \"doctor!\""
+            ,
+            "testing");
+    py_stmts(
+            "import testing\n"
+            "assert testing.a == \"doctor!\""
+            );
+
+    // however, py_stmts contextualized or without modulename does not.
+
+    py_stmts(
+            "import testing\n"
+            "a = \"nurse!\""
+            );
+    py_stmts(
+            "import testing\n"
+            "assert testing.a == \"doctor!\""
+            );
+    InterpContext c = new InterpContext();
+    c.py_stmts(
+            "import testing\n"
+            "a = \"nurse!\""
+            );
+    py_stmts(
+            "import testing\n"
+            "assert testing.a == \"doctor!\""
+            );
+
+}
+
 void main() {}

File infrastructure/deimos/python/Python.d

 module deimos.python.Python;
 
-/// _
+/// start symbol for evaluating a single statement.
 enum int Py_single_input = 256;
-/// _
+/// start symbol for evaluating multiple statements.
 enum int Py_file_input = 257;
-/// _
+/// start symbol for evaluating a single expression.
 enum int Py_eval_input = 258;
 
 version(Python_2_4_Or_Later) {

File infrastructure/deimos/python/pythonrun.d

   Mirror _pythonrun.h
 
   Interfaces to parse and execute pieces of python code 
+
+See_Also:
+<a href="http://docs.python.org/c-api/veryhigh.html"> The Very High Level Layer </a>
   */
 module deimos.python.pythonrun;
 
     char* Py_GetPythonHome();
 }
 
-/// _
+/**
+  Initialize the Python interpreter. For embedding python, this should
+  be called before accessing other Python/C API functions, with the 
+  following exceptions:
+
+  For Python 3, PyImport_AppendInittab and PyImport_ExtendInittab should
+  be called before Py_Initialize.
+  */
 void Py_Initialize();
 /// _
 void Py_InitializeEx(int);
 /// _
 node* PyParser_SimpleParseFileFlags(FILE*, const(char)*,int, int);
 
-/// _
-PyObject* PyRun_StringFlags( const(char)*, int, PyObject*, PyObject*, PyCompilerFlags*);
+    /**
+Params:
+str = python code to run
+s = start symbol. one of Py_eval_input, Py_file_input, Py_single_input.
+g = globals variables. should be a dict.
+l = local variables. should be a dict.
+flags = compilation flags (modified by `from __future__ import xx`).
+
+Returns:
+result of executing code, or null if an exception was raised.
+*/
+PyObject* PyRun_StringFlags( 
+        const(char)* str, 
+        int s, 
+        PyObject* g, 
+        PyObject* g, 
+        PyCompilerFlags* flags);
+
 version(Python_2_5_Or_Later){
-    /// _
-    PyObject* PyRun_String()(const(char)* str, int s, PyObject* g, PyObject* l) {
+    /**
+Params:
+str = python code to run
+s = start symbol. one of Py_eval_input, Py_file_input, Py_single_input.
+g = globals variables. should be a dict.
+l = local variables. should be a dict.
+
+Returns:
+result of executing code, or null if an exception was raised.
+     */
+    PyObject* PyRun_String()(
+            const(char)* str, 
+            int s, 
+            PyObject* g, 
+            PyObject* l) {
         return PyRun_StringFlags(str, s, g, l, null);
     }
     /// _
         return Py_CompileStringFlags(str, p, s, null);
     }
 }else{
-    /// _
-    PyObject* PyRun_String(const(char)*, int, PyObject*, PyObject*);
+    /**
+Params:
+str = python code to run
+s = start symbol. one of Py_eval_input, Py_file_input, Py_single_input.
+g = globals variables. should be a dict.
+l = local variables. should be a dict.
+
+Returns:
+result of executing code, or null if an exception was raised.
+*/
+    PyObject* PyRun_String(const(char)* str, int s, PyObject* g, PyObject* l);
     /// _
     PyObject* PyRun_File(FILE*, const(char)*, int, PyObject*, PyObject*);
     /// _

File infrastructure/pyd/embedded.d

     return new PydObject(PyImport_ImportModule(zcc(name)));
 }
 
+/**
+  Encapsulate a context within the Python interpreter.
+
+  This will preserve local variables and changes to the Python interpreter
+  made by 'from __future__ import feature' across calls to this.py_eval and
+  this.py_stmts.
+
+  Otherwise, will not segregate global changes made to the Python interpreter.
+  */
+class InterpContext {
+    /// dict object: global variables in this context
+    PydObject globals;
+    /// dict object: local variables in this context
+    PydObject locals;
+    PyCompilerFlags flags;
+
+    /**
+      */
+    this() {
+        debug assert(Py_IsInitialized(), "python not initialized");
+        locals = new PydObject(PyDict_New());
+        globals = py(["__builtins__": new PydObject(PyEval_GetBuiltins())]);
+    }
+
+    /**
+      Evaluate a python expression once within this context and return the 
+      result.
+
+Params:
+python = a python expression
+Returns:
+the result of expression
+     */
+    T py_eval(T = PydObject)(
+            string python, 
+            string file = __FILE__, 
+            size_t line = __LINE__) {
+
+        auto pres = PyRun_StringFlags(
+                zcc(python), 
+                Py_eval_input, 
+                cast(PyObject*) globals.ptr, 
+                cast(PyObject*) locals.ptr, 
+                &flags);
+        if(pres) {
+            auto res = new PydObject(pres);
+            return res.to_d!T();
+        }else{
+            handle_exception(file,line);
+            assert(0);
+        }
+    }
+    /**
+      Evaluate one or more python statements once.
+
+Params:
+python = python statements
+     */
+    void py_stmts(string python,
+            string file = __FILE__, 
+            size_t line = __LINE__) {
+
+        auto pres = PyRun_StringFlags(
+                zcc(python), 
+                Py_file_input, 
+                cast(PyObject*) globals.ptr, 
+                cast(PyObject*) locals.ptr,
+                &flags);
+        if(pres) {
+            Py_DECREF(pres);
+        }else{
+            handle_exception(file,line);
+        }
+    }
+}
+
 /++
 Wraps a python function (specified as a string) as a D function roughly of
 signature func_t

File infrastructure/pyd/pydobject.d

     //------------
     /// Forwards to appropriate Python binary operator overload.
     ///
-    /// Note / in python 3 always returns a floating point value,
-    /// while in python 2 returns an int for int arguments.
+    /// Note the result of / in python 3 (and python 2, if CO_FUTURE_DIVISION
+    /// is set) is interpreted as "true division", otherwise it is integer 
+    /// division for integer arguments.
+    ///
+    /// See_Also:
+    /// <a href="http://www.python.org/dev/peps/pep-0238/"> PEP 238 </a>
     PydObject opBinary(string op, T)(T o) if(op != "in") {
         static if((is(T : int) || is(T == PydObject)) && op == "*") {
             if(PySequence_Check(m_ptr)) {