See More

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(); } } } }