Skip to content

Improve method binding #974

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added automatic NuGet package generation in appveyor and local builds
- Added function that sets Py_NoSiteFlag to 1.
- Added support for Jetson Nano.
- Added support for methodbinding to IEnumerable, ICollection, IList, Task, Action, Func

### Changed

Expand Down
201 changes: 201 additions & 0 deletions src/runtime/ListWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Python.Runtime {
/// <summary>
/// Implements IEnumerable<typeparamref name="T"/> for any python iterable.
/// </summary>
internal class IterableWrapper<T> : IEnumerable<T> {
private IntPtr iterObject;

public IterableWrapper(IntPtr value) {
iterObject = Runtime.PyObject_GetIter(value);
if (iterObject == IntPtr.Zero)
Exceptions.RaiseTypeError("not an iterator");
Runtime.XIncref(iterObject);
}
~IterableWrapper() {
Runtime.XDecref(iterObject);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't use XDecref directly in destructor, see Runtime.PyObject's destructor, you can just refer a PyObject, it can let you have no need concern about about the release.

}

public IEnumerator<T> GetEnumerator() {
return GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator() {
IntPtr item;
while ((item = Runtime.PyIter_Next(iterObject)) != IntPtr.Zero) {
object obj = null;
if (!Converter.ToManaged(item, typeof(T), out obj, true)) {
Runtime.XDecref(item);
Runtime.XDecref(iterObject);
Exceptions.RaiseTypeError("wrong type in sequence");
}

Runtime.XDecref(item);
yield return obj;
}
}
}

/// <summary>
/// Implements IList<typeparamref name="T"/> for any python sequence.
/// Some methods/properties are only available on certaintypes of sequences, like list
/// </summary>
internal class ListWrapper<T> : IterableWrapper<T>, IList<T>
{
private IntPtr seq;
public ListWrapper(IntPtr value) : base(value)
{
this.seq = value;
Runtime.XIncref(value);
bool IsSeqObj = Runtime.PySequence_Check(value);
if (!IsSeqObj)
Exceptions.RaiseTypeError("not a sequence");

}
~ListWrapper()
{
Runtime.XDecref(seq);
}
public T this[int index]
{
get
{
IntPtr item = Runtime.PySequence_GetItem(seq, index);
object obj;

if (!Converter.ToManaged(item, typeof(T), out obj, true)) {
Runtime.XDecref(item);
Exceptions.RaiseTypeError("wrong type in sequence");
}

return (T)obj;
}
set
{
IntPtr pyItem = Converter.ToPython(value, typeof(T));
if (pyItem == IntPtr.Zero)
throw new Exception("failed to set item");

var result = Runtime.PySequence_SetItem(seq, index, pyItem);
Runtime.XDecref(pyItem);
if (result == -1)
throw new Exception("failed to set item");
}
}

public int Count
{
get
{
var len = Runtime.PySequence_Size(seq);
return (int)len;
}
}

public bool IsReadOnly
{
get
{
return Runtime.PyTuple_Check(seq); //python tuples are immutable
}

}

public void Add(T item) {
if (IsReadOnly)
throw new NotImplementedException();

//only implemented if this is a list!
if (!Runtime.PyList_Check(seq))
throw new NotImplementedException();

IntPtr pyItem = Converter.ToPython(item, typeof(T));
if (pyItem == IntPtr.Zero)
throw new Exception("failed to add item");

var result = Runtime.PyList_Append(seq, pyItem);
Runtime.XDecref(pyItem);
if (result == -1)
throw new Exception("failed to add item");
}

public void Clear() {
if (IsReadOnly)
throw new NotImplementedException();
var result = Runtime.PySequence_DelSlice(seq, 0, Count);
if (result == -1)
throw new Exception("failed to clear sequence");
}

public bool Contains(T item)
{
//not sure if IEquatable is implemented and this will work!
foreach (var element in this)
if (element.Equals(item)) return true;

return false;
}

public void CopyTo(T[] array, int arrayIndex)
{
for (int index = 0; index < Count; index++)
{
array[index + arrayIndex] = this[index];
}
}

public int IndexOf(T item) {
var index = 0;
foreach (var element in this) {
if (element.Equals(item)) return index;
index++;
}

return -1;
}

public void Insert(int index, T item)
{
if (IsReadOnly)
throw new NotImplementedException();

//only implemented if this is a list!
if (!Runtime.PyList_Check(seq))
throw new NotImplementedException();

IntPtr pyItem = Converter.ToPython(item, typeof(T));
if (pyItem == IntPtr.Zero)
throw new Exception("failed to insert item");

var result = Runtime.PyList_Insert(seq, index, pyItem);
Runtime.XDecref(pyItem);
if (result == -1)
throw new Exception("failed to insert item");
}

public bool InternalRemoveAt(int index)
{
if (IsReadOnly)
throw new NotImplementedException();
if (index >= Count || index < 0)
throw new IndexOutOfRangeException();

return Runtime.PySequence_DelItem(seq, index) != 0;
}

public bool Remove(T item)
{
return InternalRemoveAt(IndexOf(item));
}

public void RemoveAt(int index)
{
InternalRemoveAt(index);
}
}
}
Loading