[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