Skip to content

Commit 6bfef31

Browse files
author
Rickard Holmberg
committed
Add Runtime.PyObject_IsIterable and fix PyIter_Check for Python 2
1 parent d39f9f6 commit 6bfef31

File tree

2 files changed

+28
-13
lines changed

2 files changed

+28
-13
lines changed

src/runtime/pyobject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ public bool IsCallable()
802802
/// </remarks>
803803
public bool IsIterable()
804804
{
805-
return Runtime.PyIter_Check(obj);
805+
return Runtime.PyObject_IsIterable(obj);
806806
}
807807

808808

src/runtime/runtime.cs

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Runtime.InteropServices;
33
using System.Security;
44
using System.Text;
@@ -274,19 +274,17 @@ internal static void Initialize()
274274

275275
Error = new IntPtr(-1);
276276

277-
#if PYTHON3
278277
IntPtr dllLocal = IntPtr.Zero;
279278
if (PythonDll != "__Internal")
280279
{
281-
NativeMethods.LoadLibrary(PythonDll);
280+
dllLocal = NativeMethods.LoadLibrary(PythonDll);
282281
}
283282
_PyObject_NextNotImplemented = NativeMethods.GetProcAddress(dllLocal, "_PyObject_NextNotImplemented");
284283
#if !(MONO_LINUX || MONO_OSX)
285284
if (dllLocal != IntPtr.Zero)
286285
{
287286
NativeMethods.FreeLibrary(dllLocal);
288287
}
289-
#endif
290288
#endif
291289

292290
// Initialize modules that depend on the runtime class.
@@ -349,8 +347,8 @@ internal static int AtExit()
349347

350348
#if PYTHON3
351349
internal static IntPtr PyBytesType;
352-
internal static IntPtr _PyObject_NextNotImplemented;
353350
#endif
351+
internal static IntPtr _PyObject_NextNotImplemented;
354352

355353
internal static IntPtr PyNotImplemented;
356354
internal const int Py_LT = 0;
@@ -780,6 +778,22 @@ internal static string PyObject_GetTypeName(IntPtr op)
780778
return Marshal.PtrToStringAnsi(ppName);
781779
}
782780

781+
/// <summary>
782+
/// Test whether the Python object is an iterable.
783+
/// </summary>
784+
internal static bool PyObject_IsIterable(IntPtr pointer)
785+
{
786+
var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type);
787+
#if PYTHON2
788+
int Py_TPFLAGS_HAVE_ITER = 1 << 7;
789+
long tp_flags = Marshal.ReadInt64(ob_type, TypeOffset.tp_flags);
790+
if ((tp_flags & Py_TPFLAGS_HAVE_ITER) == 0)
791+
return false;
792+
#endif
793+
IntPtr tp_iter = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iter);
794+
return tp_iter != null;
795+
}
796+
783797
[DllImport(PythonDll, CallingConvention = CallingConvention.Cdecl)]
784798
internal static extern int PyObject_HasAttrString(IntPtr pointer, string name);
785799

@@ -1425,17 +1439,18 @@ internal static bool PyTuple_Check(IntPtr ob)
14251439
// Python iterator API
14261440
//====================================================================
14271441

1428-
#if PYTHON2
1429-
[DllImport(PythonDll, CallingConvention = CallingConvention.Cdecl)]
1430-
internal static extern bool PyIter_Check(IntPtr pointer);
1431-
#elif PYTHON3
14321442
internal static bool PyIter_Check(IntPtr pointer)
14331443
{
1434-
var ob_type = (IntPtr)Marshal.PtrToStructure(pointer + ObjectOffset.ob_type, typeof(IntPtr));
1435-
IntPtr tp_iternext = ob_type + TypeOffset.tp_iternext;
1444+
var ob_type = Marshal.ReadIntPtr(pointer, ObjectOffset.ob_type);
1445+
#if PYTHON2
1446+
int Py_TPFLAGS_HAVE_ITER = 1 << 7;
1447+
long tp_flags = Marshal.ReadInt64(ob_type, TypeOffset.tp_flags);
1448+
if ((tp_flags & Py_TPFLAGS_HAVE_ITER) == 0)
1449+
return false;
1450+
#endif
1451+
IntPtr tp_iternext = Marshal.ReadIntPtr(ob_type, TypeOffset.tp_iternext);
14361452
return tp_iternext != null && tp_iternext != _PyObject_NextNotImplemented;
14371453
}
1438-
#endif
14391454

14401455
[DllImport(PythonDll, CallingConvention = CallingConvention.Cdecl)]
14411456
internal static extern IntPtr PyIter_Next(IntPtr pointer);

0 commit comments

Comments
 (0)