[Python.NET] pythonnet-2.0-alpha2: PyString cannot create 8-bit Python strings

Adrian Buehlmann adrian at cadifra.com
Wed Jan 9 20:18:56 CET 2008


Hi all

While trying to call Python code from within C# using "Python.Runtime" from
pythonnet-2.0-alpha2, I ran into a problem using "PyString", which can only
create *unicode* python strings and I needed a way to create 8-bit (single
byte character) Python strings in order to call "PyObject.Invoke(PyTuple,
PyDict)". Detailed report follows below. [Side note: .Net "string" (which is
"System.String") is a unicode string]

I've tried calling Python code from within C# code using:

   Python.Runtime.PyObject.Invoke(PyTuple, PyDict)

I prepared the PyDict by using:

   Python.Runtime.PyObject.this[string]

The original code I tried was:

<code-snip-1>
   PythonEngine.Initialize();
   IntPtr lck = PythonEngine.AcquireLock();

   PyObject mod_builtin = PythonEngine.ImportModule("__builtin__");
   PyObject False = mod_builtin.GetAttr("False");

   PyObject mod_hg = PythonEngine.ImportModule("mercurial");
   PyObject mod_ui = PythonEngine.ImportModule("mercurial.ui");
   PyObject ui_ctor = mod_ui.GetAttr("ui");

   PyDict d = new PyDict();
   d["interactive"] = False;
   PyTuple noargs = new PyTuple();

   PyObject ui = ui_ctor.Invoke(noargs, d);
</code-snip-1>

(For the Mercurial details see:
http://www.selenic.com/mercurial/wiki/
http://selenic.com/repo/hg/file/23889160905a/mercurial/ui.py)

The relevant called python code is:

<code-snip-2>
class ui(object):
     def __init__(self, verbose=False, debug=False, quiet=False,
                  interactive=True, traceback=False,
                  report_untrusted=True, parentui=None):
</code-snip-2>

Problem is, my code-snip-1 didn't work, because that adds
a *unicode* key string to the dict, because it uses:

   Python.Runtime.PyString.PyString(string)

which creates a *unicode" python string. But Invoke() works only when using
8-bit (single byte) strings for the keys in the dict.

To fix this I changed "src/runtime/pydict.cs" (mercurial patch):

<hg-patch1>
# HG changeset patch
# User Adrian Buehlmann
# Date 1199891100 -3600
# Node ID 89623ba76fd3d72c56e05981f2504dd2872560fb
# Parent  3c3b7151ad596a4e6cf3012c33246418c641a66d
New SetItemString() on PyDict

diff -r 3c3b7151ad59 -r 89623ba76fd3 runtime/pydict.cs
--- a/runtime/pydict.cs Wed Jan 09 15:59:24 2008 +0100
+++ b/runtime/pydict.cs Wed Jan 09 16:05:00 2008 +0100
@@ -201,6 +201,22 @@ namespace Python.Runtime {
          public void Clear() {
              Runtime.PyDict_Clear(obj);
          }
+
+
+        /// <summary>
+        /// SetItemString Method
+        /// </summary>
+        ///
+        /// <remarks>
+        /// Inserts value into this dictionary using ansi string key as a key.
+        /// </remarks>
+
+        public void SetItemString(string key, PyObject value) {
+            int result = Runtime.PyDict_SetItemString(obj, key, value.obj);
+            if (result < 0) {
+                throw new PythonException();
+            }
+        }


      }
</hg-patch1>

which adds a new SetItemString() method to PyDict and then did:

<code-snip-3>
   PythonEngine.Initialize();
   IntPtr lck = PythonEngine.AcquireLock();

   PyObject mod_builtin = PythonEngine.ImportModule("__builtin__");
   PyObject False = mod_builtin.GetAttr("False");

   PyObject mod_hg = PythonEngine.ImportModule("mercurial");
   PyObject mod_ui = PythonEngine.ImportModule("mercurial.ui");
   PyObject ui_ctor = mod_ui.GetAttr("ui");

   PyDict d = new PyDict();
   d.SetItemString("interactive", False);
   PyTuple noargs = new PyTuple();

   PyObject ui = ui_ctor.Invoke(noargs, d);
</code-snip-3>

I didn't see a better way to create a PyObject that represents an ansi python
string. With PyString I can only create *unicode* python strings.

I was tempted to do:

<code-snip-4>
     public PyString(string s) : base() {
         // obj = Runtime.PyUnicode_FromUnicode(s, s.Length);  // orig code
         obj = Runtime.PyString_FromString(s);  // create ansi string instead
         if (obj == IntPtr.Zero) {
             throw new PythonException();
         }
     }
</code-snip-4>

but that would break existing code (not mine).

Adrian










More information about the PythonDotNet mailing list