using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
namespace Python.Runtime
{
public interface IPyDisposable : IDisposable
{
IntPtr[] GetTrackedHandles();
}
///
/// Represents a generic Python object. The methods of this class are
/// generally equivalent to the Python "abstract object API". See
/// PY2: https://docs.python.org/2/c-api/object.html
/// PY3: https://docs.python.org/3/c-api/object.html
/// for details.
///
[Serializable]
public partial class PyObject : DynamicObject, IEnumerable, IPyDisposable
{
#if TRACE_ALLOC
///
/// Trace stack for PyObject's construction
///
public StackTrace Traceback { get; private set; }
#endif
protected internal IntPtr obj = IntPtr.Zero;
internal BorrowedReference Reference => new BorrowedReference(obj);
///
/// PyObject Constructor
///
///
/// Creates a new PyObject from an IntPtr object reference. Note that
/// the PyObject instance assumes ownership of the object reference
/// and the reference will be DECREFed when the PyObject is garbage
/// collected or explicitly disposed.
///
public PyObject(IntPtr ptr)
{
if (ptr == IntPtr.Zero) throw new ArgumentNullException(nameof(ptr));
obj = ptr;
Finalizer.Instance.ThrottledCollect();
#if TRACE_ALLOC
Traceback = new StackTrace(1);
#endif
}
///
/// Creates new pointing to the same object as
/// the . Increments refcount, allowing
/// to have ownership over its own reference.
///
internal PyObject(BorrowedReference reference)
{
if (reference.IsNull) throw new ArgumentNullException(nameof(reference));
obj = Runtime.SelfIncRef(reference.DangerousGetAddress());
Finalizer.Instance.ThrottledCollect();
#if TRACE_ALLOC
Traceback = new StackTrace(1);
#endif
}
// Ensure that encapsulated Python object is decref'ed appropriately
// when the managed wrapper is garbage-collected.
~PyObject()
{
if (obj == IntPtr.Zero)
{
return;
}
Finalizer.Instance.AddFinalizedObject(this);
}
///
/// Handle Property
///
///
/// Gets the native handle of the underlying Python object. This
/// value is generally for internal use by the PythonNet runtime.
///
public IntPtr Handle
{
get { return obj; }
}
///
/// Gets raw Python proxy for this object (bypasses all conversions,
/// except null <==> None)
///
///
/// Given an arbitrary managed object, return a Python instance that
/// reflects the managed object.
///
public static PyObject FromManagedObject(object ob)
{
// Special case: if ob is null, we return None.
if (ob == null)
{
Runtime.XIncref(Runtime.PyNone);
return new PyObject(Runtime.PyNone);
}
IntPtr op = CLRObject.GetInstHandle(ob);
return new PyObject(op);
}
///
/// AsManagedObject Method
///
///
/// Return a managed object of the given type, based on the
/// value of the Python object.
///
public object AsManagedObject(Type t)
{
object result;
if (!Converter.ToManaged(obj, t, out result, false))
{
throw new InvalidCastException("cannot convert object to target type");
}
return result;
}
///
/// As Method
///
///
/// Return a managed object of the given type, based on the
/// value of the Python object.
///
public T As()
{
if (typeof(T) == typeof(PyObject) || typeof(T) == typeof(object))
{
return (T)(this as object);
}
object result;
if (!Converter.ToManaged(obj, typeof(T), out result, false))
{
throw new InvalidCastException("cannot convert object to target type");
}
return (T)result;
}
///
/// Dispose Method
///
///
/// The Dispose method provides a way to explicitly release the
/// Python object represented by a PyObject instance. It is a good
/// idea to call Dispose on PyObjects that wrap resources that are
/// limited or need strict lifetime control. Otherwise, references
/// to Python objects will not be released until a managed garbage
/// collection occurs.
///
protected virtual void Dispose(bool disposing)
{
if (this.obj == IntPtr.Zero)
{
return;
}
if (Runtime.Py_IsInitialized() == 0)
throw new InvalidOperationException("Python runtime must be initialized");
if (!Runtime.IsFinalizing)
{
long refcount = Runtime.Refcount(this.obj);
Debug.Assert(refcount > 0, "Object refcount is 0 or less");
if (refcount == 1)
{
Runtime.PyErr_Fetch(out var errType, out var errVal, out var traceback);
try
{
Runtime.XDecref(this.obj);
Runtime.CheckExceptionOccurred();
}
finally
{
// Python requires finalizers to preserve exception:
// https://docs.python.org/3/extending/newtypes.html#finalization-and-de-allocation
Runtime.PyErr_Restore(errType, errVal, traceback);
}
}
else
{
Runtime.XDecref(this.obj);
}
}
this.obj = IntPtr.Zero;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public IntPtr[] GetTrackedHandles()
{
return new IntPtr[] { obj };
}
///
/// GetPythonType Method
///
///
/// Returns the Python type of the object. This method is equivalent
/// to the Python expression: type(object).
///
public PyObject GetPythonType()
{
IntPtr tp = Runtime.PyObject_Type(obj);
return new PyObject(tp);
}
///
/// TypeCheck Method
///
///
/// Returns true if the object o is of type typeOrClass or a subtype
/// of typeOrClass.
///
public bool TypeCheck(PyObject typeOrClass)
{
if (typeOrClass == null) throw new ArgumentNullException(nameof(typeOrClass));
return Runtime.PyObject_TypeCheck(obj, typeOrClass.obj);
}
///
/// HasAttr Method
///
///
/// Returns true if the object has an attribute with the given name.
///
public bool HasAttr(string name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
return Runtime.PyObject_HasAttrString(obj, name) != 0;
}
///
/// HasAttr Method
///
///
/// Returns true if the object has an attribute with the given name,
/// where name is a PyObject wrapping a string or unicode object.
///
public bool HasAttr(PyObject name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
return Runtime.PyObject_HasAttr(obj, name.obj) != 0;
}
///
/// GetAttr Method
///
///
/// Returns the named attribute of the Python object, or raises a
/// PythonException if the attribute access fails.
///
public PyObject GetAttr(string name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
IntPtr op = Runtime.PyObject_GetAttrString(obj, name);
if (op == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(op);
}
///
/// GetAttr Method. Returns fallback value if getting attribute fails for any reason.
///
///
/// Returns the named attribute of the Python object, or the given
/// default object if the attribute access fails.
///
public PyObject GetAttr(string name, PyObject _default)
{
if (name == null) throw new ArgumentNullException(nameof(name));
IntPtr op = Runtime.PyObject_GetAttrString(obj, name);
if (op == IntPtr.Zero)
{
Runtime.PyErr_Clear();
return _default;
}
return new PyObject(op);
}
///
/// GetAttr Method
///
///
/// Returns the named attribute of the Python object or raises a
/// PythonException if the attribute access fails. The name argument
/// is a PyObject wrapping a Python string or unicode object.
///
public PyObject GetAttr(PyObject name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
IntPtr op = Runtime.PyObject_GetAttr(obj, name.obj);
if (op == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(op);
}
///
/// GetAttr Method
///
///
/// Returns the named attribute of the Python object, or the given
/// default object if the attribute access fails. The name argument
/// is a PyObject wrapping a Python string or unicode object.
///
public PyObject GetAttr(PyObject name, PyObject _default)
{
if (name == null) throw new ArgumentNullException(nameof(name));
IntPtr op = Runtime.PyObject_GetAttr(obj, name.obj);
if (op == IntPtr.Zero)
{
Runtime.PyErr_Clear();
return _default;
}
return new PyObject(op);
}
///
/// SetAttr Method
///
///
/// Set an attribute of the object with the given name and value. This
/// method throws a PythonException if the attribute set fails.
///
public void SetAttr(string name, PyObject value)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (value == null) throw new ArgumentNullException(nameof(value));
int r = Runtime.PyObject_SetAttrString(obj, name, value.obj);
if (r < 0)
{
throw new PythonException();
}
}
///
/// SetAttr Method
///
///
/// Set an attribute of the object with the given name and value,
/// where the name is a Python string or unicode object. This method
/// throws a PythonException if the attribute set fails.
///
public void SetAttr(PyObject name, PyObject value)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (value == null) throw new ArgumentNullException(nameof(value));
int r = Runtime.PyObject_SetAttr(obj, name.obj, value.obj);
if (r < 0)
{
throw new PythonException();
}
}
///
/// DelAttr Method
///
///
/// Delete the named attribute of the Python object. This method
/// throws a PythonException if the attribute set fails.
///
public void DelAttr(string name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
int r = Runtime.PyObject_SetAttrString(obj, name, IntPtr.Zero);
if (r < 0)
{
throw new PythonException();
}
}
///
/// DelAttr Method
///
///
/// Delete the named attribute of the Python object, where name is a
/// PyObject wrapping a Python string or unicode object. This method
/// throws a PythonException if the attribute set fails.
///
public void DelAttr(PyObject name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
int r = Runtime.PyObject_SetAttr(obj, name.obj, IntPtr.Zero);
if (r < 0)
{
throw new PythonException();
}
}
///
/// GetItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// return the item at the given object index. This method raises a
/// PythonException if the indexing operation fails.
///
public virtual PyObject GetItem(PyObject key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
IntPtr op = Runtime.PyObject_GetItem(obj, key.obj);
if (op == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(op);
}
///
/// GetItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// return the item at the given string index. This method raises a
/// PythonException if the indexing operation fails.
///
public virtual PyObject GetItem(string key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
using (var pyKey = new PyString(key))
{
return GetItem(pyKey);
}
}
///
/// GetItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// return the item at the given numeric index. This method raises a
/// PythonException if the indexing operation fails.
///
public virtual PyObject GetItem(int index)
{
using (var key = new PyInt(index))
{
return GetItem(key);
}
}
///
/// SetItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// set the item at the given object index to the given value. This
/// method raises a PythonException if the set operation fails.
///
public virtual void SetItem(PyObject key, PyObject value)
{
if (key == null) throw new ArgumentNullException(nameof(key));
if (value == null) throw new ArgumentNullException(nameof(value));
int r = Runtime.PyObject_SetItem(obj, key.obj, value.obj);
if (r < 0)
{
throw new PythonException();
}
}
///
/// SetItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// set the item at the given string index to the given value. This
/// method raises a PythonException if the set operation fails.
///
public virtual void SetItem(string key, PyObject value)
{
if (key == null) throw new ArgumentNullException(nameof(key));
if (value == null) throw new ArgumentNullException(nameof(value));
using (var pyKey = new PyString(key))
{
SetItem(pyKey, value);
}
}
///
/// SetItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// set the item at the given numeric index to the given value. This
/// method raises a PythonException if the set operation fails.
///
public virtual void SetItem(int index, PyObject value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
using (var pyindex = new PyInt(index))
{
SetItem(pyindex, value);
}
}
///
/// DelItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// delete the item at the given object index. This method raises a
/// PythonException if the delete operation fails.
///
public virtual void DelItem(PyObject key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
int r = Runtime.PyObject_DelItem(obj, key.obj);
if (r < 0)
{
throw new PythonException();
}
}
///
/// DelItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// delete the item at the given string index. This method raises a
/// PythonException if the delete operation fails.
///
public virtual void DelItem(string key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
using (var pyKey = new PyString(key))
{
DelItem(pyKey);
}
}
///
/// DelItem Method
///
///
/// For objects that support the Python sequence or mapping protocols,
/// delete the item at the given numeric index. This method raises a
/// PythonException if the delete operation fails.
///
public virtual void DelItem(int index)
{
using (var pyindex = new PyInt(index))
{
DelItem(pyindex);
}
}
///
/// Length Method
///
///
/// Returns the length for objects that support the Python sequence
/// protocol, or 0 if the object does not support the protocol.
///
public virtual long Length()
{
var s = Runtime.PyObject_Size(obj);
if (s < 0)
{
Runtime.PyErr_Clear();
return 0;
}
return s;
}
///
/// String Indexer
///
///
/// Provides a shorthand for the string versions of the GetItem and
/// SetItem methods.
///
public virtual PyObject this[string key]
{
get { return GetItem(key); }
set { SetItem(key, value); }
}
///
/// PyObject Indexer
///
///
/// Provides a shorthand for the object versions of the GetItem and
/// SetItem methods.
///
public virtual PyObject this[PyObject key]
{
get { return GetItem(key); }
set { SetItem(key, value); }
}
///
/// Numeric Indexer
///
///
/// Provides a shorthand for the numeric versions of the GetItem and
/// SetItem methods.
///
public virtual PyObject this[int index]
{
get { return GetItem(index); }
set { SetItem(index, value); }
}
///
/// GetIterator Method
///
///
/// Return a new (Python) iterator for the object. This is equivalent
/// to the Python expression "iter(object)". A PythonException will be
/// raised if the object cannot be iterated.
///
public PyObject GetIterator()
{
IntPtr r = Runtime.PyObject_GetIter(obj);
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
///
/// GetEnumerator Method
///
///
/// Return a new PyIter object for the object. This allows any iterable
/// python object to be iterated over in C#. A PythonException will be
/// raised if the object is not iterable.
///
public IEnumerator GetEnumerator()
{
return PyIter.GetIter(this);
}
///
/// Invoke Method
///
///
/// Invoke the callable object with the given arguments, passed as a
/// PyObject[]. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(params PyObject[] args)
{
if (args == null) throw new ArgumentNullException(nameof(args));
if (args.Contains(null)) throw new ArgumentNullException();
var t = new PyTuple(args);
IntPtr r = Runtime.PyObject_Call(obj, t.obj, IntPtr.Zero);
t.Dispose();
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
///
/// Invoke Method
///
///
/// Invoke the callable object with the given arguments, passed as a
/// Python tuple. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(PyTuple args)
{
if (args == null) throw new ArgumentNullException(nameof(args));
IntPtr r = Runtime.PyObject_Call(obj, args.obj, IntPtr.Zero);
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
///
/// Invoke Method
///
///
/// Invoke the callable object with the given positional and keyword
/// arguments. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(PyObject[] args, PyDict kw)
{
if (args == null) throw new ArgumentNullException(nameof(args));
if (args.Contains(null)) throw new ArgumentNullException();
var t = new PyTuple(args);
IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw?.obj ?? IntPtr.Zero);
t.Dispose();
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
///
/// Invoke Method
///
///
/// Invoke the callable object with the given positional and keyword
/// arguments. A PythonException is raised if the invocation fails.
///
public PyObject Invoke(PyTuple args, PyDict kw)
{
if (args == null) throw new ArgumentNullException(nameof(args));
IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw?.obj ?? IntPtr.Zero);
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyObject(r);
}
///
/// InvokeMethod Method
///
///
/// Invoke the named method of the object with the given arguments.
/// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, params PyObject[] args)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (args == null) throw new ArgumentNullException(nameof(args));
if (args.Contains(null)) throw new ArgumentNullException();
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
return result;
}
///
/// InvokeMethod Method
///
///
/// Invoke the named method of the object with the given arguments.
/// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, PyTuple args)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (args == null) throw new ArgumentNullException(nameof(args));
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
return result;
}
///
/// InvokeMethod Method
///
///
/// Invoke the named method of the object with the given arguments.
/// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(PyObject name, params PyObject[] args)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (args == null) throw new ArgumentNullException(nameof(args));
if (args.Contains(null)) throw new ArgumentNullException();
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
return result;
}
///
/// InvokeMethod Method
///
///
/// Invoke the named method of the object with the given arguments.
/// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(PyObject name, PyTuple args)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (args == null) throw new ArgumentNullException(nameof(args));
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args);
method.Dispose();
return result;
}
///
/// InvokeMethod Method
///
///
/// Invoke the named method of the object with the given arguments
/// and keyword arguments. Keyword args are passed as a PyDict object.
/// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, PyObject[] args, PyDict kw)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (args == null) throw new ArgumentNullException(nameof(args));
if (args.Contains(null)) throw new ArgumentNullException();
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args, kw);
method.Dispose();
return result;
}
///
/// InvokeMethod Method
///
///
/// Invoke the named method of the object with the given arguments
/// and keyword arguments. Keyword args are passed as a PyDict object.
/// A PythonException is raised if the invocation is unsuccessful.
///
public PyObject InvokeMethod(string name, PyTuple args, PyDict kw)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (args == null) throw new ArgumentNullException(nameof(args));
PyObject method = GetAttr(name);
PyObject result = method.Invoke(args, kw);
method.Dispose();
return result;
}
///
/// IsInstance Method
///
///
/// Return true if the object is an instance of the given Python type
/// or class. This method always succeeds.
///
public bool IsInstance(PyObject typeOrClass)
{
if (typeOrClass == null) throw new ArgumentNullException(nameof(typeOrClass));
int r = Runtime.PyObject_IsInstance(obj, typeOrClass.obj);
if (r < 0)
{
Runtime.PyErr_Clear();
return false;
}
return r != 0;
}
///
/// IsSubclass Method
///
///
/// Return true if the object is identical to or derived from the
/// given Python type or class. This method always succeeds.
///
public bool IsSubclass(PyObject typeOrClass)
{
if (typeOrClass == null) throw new ArgumentNullException(nameof(typeOrClass));
int r = Runtime.PyObject_IsSubclass(obj, typeOrClass.obj);
if (r < 0)
{
Runtime.PyErr_Clear();
return false;
}
return r != 0;
}
///
/// IsCallable Method
///
///
/// Returns true if the object is a callable object. This method
/// always succeeds.
///
public bool IsCallable()
{
return Runtime.PyCallable_Check(obj) != 0;
}
///
/// IsIterable Method
///
///
/// Returns true if the object is iterable object. This method
/// always succeeds.
///
public bool IsIterable()
{
return Runtime.PyObject_IsIterable(obj);
}
///
/// IsTrue Method
///
///
/// Return true if the object is true according to Python semantics.
/// This method always succeeds.
///
public bool IsTrue()
{
return Runtime.PyObject_IsTrue(obj) != 0;
}
///
/// Return true if the object is None
///
public bool IsNone() => CheckNone(this) == null;
///
/// Dir Method
///
///
/// Return a list of the names of the attributes of the object. This
/// is equivalent to the Python expression "dir(object)".
///
public PyList Dir()
{
IntPtr r = Runtime.PyObject_Dir(obj);
if (r == IntPtr.Zero)
{
throw new PythonException();
}
return new PyList(r);
}
///
/// Repr Method
///
///
/// Return a string representation of the object. This method is
/// the managed equivalent of the Python expression "repr(object)".
///
public string Repr()
{
IntPtr strval = Runtime.PyObject_Repr(obj);
string result = Runtime.GetManagedString(strval);
Runtime.XDecref(strval);
return result;
}
///
/// ToString Method
///
///
/// Return the string representation of the object. This method is
/// the managed equivalent of the Python expression "str(object)".
///
public override string ToString()
{
IntPtr strval = Runtime.PyObject_Unicode(obj);
string result = Runtime.GetManagedString(strval);
Runtime.XDecref(strval);
return result;
}
///
/// Equals Method
///
///
/// Return true if this object is equal to the given object. This
/// method is based on Python equality semantics.
///
public override bool Equals(object o)
{
if (!(o is PyObject))
{
return false;
}
if (obj == ((PyObject)o).obj)
{
return true;
}
int r = Runtime.PyObject_Compare(obj, ((PyObject)o).obj);
if (Exceptions.ErrorOccurred())
{
throw new PythonException();
}
return r == 0;
}
///
/// GetHashCode Method
///
///
/// Return a hashcode based on the Python object. This returns the
/// hash as computed by Python, equivalent to the Python expression
/// "hash(obj)".
///
public override int GetHashCode()
{
return ((ulong)Runtime.PyObject_Hash(obj)).GetHashCode();
}
///
/// GetBuffer Method. This Method only works for objects that have a buffer (like "bytes", "bytearray" or "array.array")
///
///
/// Send a request to the PyObject to fill in view as specified by flags. If the PyObject cannot provide a buffer of the exact type, it MUST raise PyExc_BufferError, set view->obj to NULL and return -1.
/// On success, fill in view, set view->obj to a new reference to exporter and return 0. In the case of chained buffer providers that redirect requests to a single object, view->obj MAY refer to this object instead of exporter(See Buffer Object Structures).
/// Successful calls to must be paired with calls to , similar to malloc() and free(). Thus, after the consumer is done with the buffer, must be called exactly once.
///
public PyBuffer GetBuffer(PyBUF flags = PyBUF.SIMPLE)
{
return new PyBuffer(this, flags);
}
public long Refcount
{
get
{
return Runtime.Refcount(obj);
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = CheckNone(this.GetAttr(binder.Name));
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
IntPtr ptr = Converter.ToPython(value, value?.GetType());
int r = Runtime.PyObject_SetAttrString(obj, binder.Name, ptr);
if (r < 0)
{
throw new PythonException();
}
Runtime.XDecref(ptr);
return true;
}
private void GetArgs(object[] inargs, CallInfo callInfo, out PyTuple args, out PyDict kwargs)
{
if (callInfo == null || callInfo.ArgumentNames.Count == 0)
{
GetArgs(inargs, out args, out kwargs);
return;
}
// Support for .net named arguments
var namedArgumentCount = callInfo.ArgumentNames.Count;
var regularArgumentCount = callInfo.ArgumentCount - namedArgumentCount;
var argTuple = Runtime.PyTuple_New(regularArgumentCount);
for (int i = 0; i < regularArgumentCount; ++i)
{
AddArgument(argTuple, i, inargs[i]);
}
args = new PyTuple(argTuple);
var namedArgs = new object[namedArgumentCount * 2];
for (int i = 0; i < namedArgumentCount; ++i)
{
namedArgs[i * 2] = callInfo.ArgumentNames[i];
namedArgs[i * 2 + 1] = inargs[regularArgumentCount + i];
}
kwargs = Py.kw(namedArgs);
}
private void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs)
{
int arg_count;
for (arg_count = 0; arg_count < inargs.Length && !(inargs[arg_count] is Py.KeywordArguments); ++arg_count)
{
;
}
IntPtr argtuple = Runtime.PyTuple_New(arg_count);
for (var i = 0; i < arg_count; i++)
{
AddArgument(argtuple, i, inargs[i]);
}
args = new PyTuple(argtuple);
kwargs = null;
for (int i = arg_count; i < inargs.Length; i++)
{
if (!(inargs[i] is Py.KeywordArguments))
{
throw new ArgumentException("Keyword arguments must come after normal arguments.");
}
if (kwargs == null)
{
kwargs = (Py.KeywordArguments)inargs[i];
}
else
{
kwargs.Update((Py.KeywordArguments)inargs[i]);
}
}
}
private static void AddArgument(IntPtr argtuple, int i, object target)
{
IntPtr ptr = GetPythonObject(target);
if (Runtime.PyTuple_SetItem(argtuple, i, ptr) < 0)
{
throw new PythonException();
}
}
private static IntPtr GetPythonObject(object target)
{
IntPtr ptr;
if (target is PyObject)
{
ptr = ((PyObject)target).Handle;
Runtime.XIncref(ptr);
}
else
{
ptr = Converter.ToPython(target, target?.GetType());
}
return ptr;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (this.HasAttr(binder.Name) && this.GetAttr(binder.Name).IsCallable())
{
PyTuple pyargs = null;
PyDict kwargs = null;
try
{
GetArgs(args, binder.CallInfo, out pyargs, out kwargs);
result = CheckNone(InvokeMethod(binder.Name, pyargs, kwargs));
}
finally
{
if (null != pyargs)
{
pyargs.Dispose();
}
if (null != kwargs)
{
kwargs.Dispose();
}
}
return true;
}
else
{
return base.TryInvokeMember(binder, args, out result);
}
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
if (this.IsCallable())
{
PyTuple pyargs = null;
PyDict kwargs = null;
try
{
GetArgs(args, binder.CallInfo, out pyargs, out kwargs);
result = CheckNone(Invoke(pyargs, kwargs));
}
finally
{
if (null != pyargs)
{
pyargs.Dispose();
}
if (null != kwargs)
{
kwargs.Dispose();
}
}
return true;
}
else
{
return base.TryInvoke(binder, args, out result);
}
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
return Converter.ToManaged(this.obj, binder.Type, out result, false);
}
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
{
IntPtr res;
if (!(arg is PyObject))
{
arg = arg.ToPython();
}
switch (binder.Operation)
{
case ExpressionType.Add:
res = Runtime.PyNumber_Add(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.AddAssign:
res = Runtime.PyNumber_InPlaceAdd(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.Subtract:
res = Runtime.PyNumber_Subtract(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.SubtractAssign:
res = Runtime.PyNumber_InPlaceSubtract(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.Multiply:
res = Runtime.PyNumber_Multiply(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.MultiplyAssign:
res = Runtime.PyNumber_InPlaceMultiply(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.Divide:
res = Runtime.PyNumber_TrueDivide(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.DivideAssign:
res = Runtime.PyNumber_InPlaceTrueDivide(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.And:
res = Runtime.PyNumber_And(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.AndAssign:
res = Runtime.PyNumber_InPlaceAnd(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.ExclusiveOr:
res = Runtime.PyNumber_Xor(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.ExclusiveOrAssign:
res = Runtime.PyNumber_InPlaceXor(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.GreaterThan:
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) > 0;
return true;
case ExpressionType.GreaterThanOrEqual:
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) >= 0;
return true;
case ExpressionType.LeftShift:
res = Runtime.PyNumber_Lshift(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.LeftShiftAssign:
res = Runtime.PyNumber_InPlaceLshift(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.LessThan:
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) < 0;
return true;
case ExpressionType.LessThanOrEqual:
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) <= 0;
return true;
case ExpressionType.Modulo:
res = Runtime.PyNumber_Remainder(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.ModuloAssign:
res = Runtime.PyNumber_InPlaceRemainder(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.NotEqual:
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) != 0;
return true;
case ExpressionType.Or:
res = Runtime.PyNumber_Or(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.OrAssign:
res = Runtime.PyNumber_InPlaceOr(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.Power:
res = Runtime.PyNumber_Power(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.RightShift:
res = Runtime.PyNumber_Rshift(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.RightShiftAssign:
res = Runtime.PyNumber_InPlaceRshift(this.obj, ((PyObject)arg).obj);
break;
default:
result = null;
return false;
}
result = CheckNone(new PyObject(res));
return true;
}
// Workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=41509
// See https://github.com/pythonnet/pythonnet/pull/219
private static object CheckNone(PyObject pyObj)
{
if (pyObj != null)
{
if (pyObj.obj == Runtime.PyNone)
{
return null;
}
}
return pyObj;
}
public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result)
{
int r;
IntPtr res;
switch (binder.Operation)
{
case ExpressionType.Negate:
res = Runtime.PyNumber_Negative(this.obj);
break;
case ExpressionType.UnaryPlus:
res = Runtime.PyNumber_Positive(this.obj);
break;
case ExpressionType.OnesComplement:
res = Runtime.PyNumber_Invert(this.obj);
break;
case ExpressionType.Not:
r = Runtime.PyObject_Not(this.obj);
result = r == 1;
return r != -1;
case ExpressionType.IsFalse:
r = Runtime.PyObject_IsTrue(this.obj);
result = r == 0;
return r != -1;
case ExpressionType.IsTrue:
r = Runtime.PyObject_IsTrue(this.obj);
result = r == 1;
return r != -1;
case ExpressionType.Decrement:
case ExpressionType.Increment:
default:
result = null;
return false;
}
result = CheckNone(new PyObject(res));
return true;
}
///
/// Returns the enumeration of all dynamic member names.
///
///
/// This method exists for debugging purposes only.
///
/// A sequence that contains dynamic member names.
public override IEnumerable GetDynamicMemberNames()
{
foreach (PyObject pyObj in Dir())
{
yield return pyObj.ToString();
}
}
}
}