using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Threading;
namespace Python.Runtime
{
///
/// 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]
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")]
public partial class PyObject : DynamicObject, IDisposable, ISerializable
{
#if TRACE_ALLOC
///
/// Trace stack for PyObject's construction
///
public StackTrace Traceback { get; } = new StackTrace(1);
#endif
protected IntPtr rawPtr = IntPtr.Zero;
internal readonly int run = Runtime.GetRun();
internal BorrowedReference obj => new (rawPtr);
public static PyObject None => new (Runtime.PyNone);
internal BorrowedReference Reference => new (rawPtr);
///
/// 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.
///
[Obsolete]
internal PyObject(IntPtr ptr)
{
if (ptr == IntPtr.Zero) throw new ArgumentNullException(nameof(ptr));
rawPtr = ptr;
Finalizer.Instance.ThrottledCollect();
}
[Obsolete("for testing purposes only")]
internal PyObject(IntPtr ptr, bool skipCollect)
{
if (ptr == IntPtr.Zero) throw new ArgumentNullException(nameof(ptr));
rawPtr = ptr;
if (!skipCollect)
Finalizer.Instance.ThrottledCollect();
}
///
/// 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));
rawPtr = new NewReference(reference).DangerousMoveToPointer();
Finalizer.Instance.ThrottledCollect();
}
internal PyObject(BorrowedReference reference, bool skipCollect)
{
if (reference.IsNull) throw new ArgumentNullException(nameof(reference));
rawPtr = new NewReference(reference).DangerousMoveToPointer();
if (!skipCollect)
Finalizer.Instance.ThrottledCollect();
}
internal PyObject(in StolenReference reference)
{
if (reference == null) throw new ArgumentNullException(nameof(reference));
rawPtr = reference.DangerousGetAddressOrNull();
Finalizer.Instance.ThrottledCollect();
}
// Ensure that encapsulated Python object is decref'ed appropriately
// when the managed wrapper is garbage-collected.
~PyObject()
{
if (!IsDisposed)
{
#if TRACE_ALLOC
CheckRun();
#endif
Interlocked.Increment(ref Runtime._collected);
Finalizer.Instance.AddFinalizedObject(ref rawPtr, run
#if TRACE_ALLOC
, Traceback
#endif
);
}
Dispose(false);
}
///
/// Gets the native handle of the underlying Python object. This
/// value is generally for internal use by the PythonNet runtime.
///
[Obsolete]
public IntPtr Handle
{
get { return rawPtr; }
}
///
/// 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)
{
return new PyObject(Runtime.PyNone);
}
return CLRObject.GetReference(ob).MoveToPyObject();
}
///
/// Creates new from a nullable reference.
/// When is null, null is returned.
///
internal static PyObject? FromNullableReference(BorrowedReference reference)
=> reference.IsNull ? null : new PyObject(reference);
///
/// AsManagedObject Method
///
///
/// Return a managed object of the given type, based on the
/// value of the Python object.
///
public object? AsManagedObject(Type t)
{
if (!Converter.ToManaged(obj, t, out var result, true))
{
throw new InvalidCastException("cannot convert object to target type",
PythonException.FetchCurrentOrNull(out _));
}
return result;
}
///
/// Return a managed object of the given type, based on the
/// value of the Python object.
///
public T As() => (T)this.AsManagedObject(typeof(T))!;
internal bool IsDisposed => rawPtr == IntPtr.Zero;
void CheckDisposed()
{
if (IsDisposed) throw new ObjectDisposedException(nameof(PyObject));
}
protected virtual void Dispose(bool disposing)
{
if (IsDisposed)
{
return;
}
if (Runtime.Py_IsInitialized() == 0 && Runtime._Py_IsFinalizing() != true)
{
throw new InvalidOperationException("Python runtime must be initialized");
}
nint 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(StolenReference.Take(ref rawPtr));
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.StealNullable(), errVal.StealNullable(), traceback.StealNullable());
}
}
else
{
Runtime.XDecref(StolenReference.Take(ref rawPtr));
}
this.rawPtr = IntPtr.Zero;
}
///
/// 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.
///
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
internal StolenReference Steal()
{
GC.SuppressFinalize(this);
return StolenReference.Take(ref this.rawPtr);
}
[Obsolete("Test use only")]
internal void Leak()
{
Debug.Assert(!IsDisposed);
GC.SuppressFinalize(this);
rawPtr = IntPtr.Zero;
}
internal IntPtr DangerousGetAddressOrNull() => rawPtr;
internal void CheckRun()
{
if (run != Runtime.GetRun())
throw new RuntimeShutdownException(rawPtr);
}
internal BorrowedReference GetPythonTypeReference()
=> Runtime.PyObject_TYPE(obj);
///
/// GetPythonType Method
///
///
/// Returns the Python type of the object. This method is equivalent
/// to the Python expression: type(object).
///
public PyType GetPythonType()
{
var tp = Runtime.PyObject_Type(Reference);
return new PyType(tp.StealOrThrow(), prevalidated: true);
}
///
/// TypeCheck Method
///
///
/// Returns true if the object o is of type typeOrClass or a subtype
/// of typeOrClass.
///
public bool TypeCheck(PyType typeOrClass)
{
if (typeOrClass == null) throw new ArgumentNullException(nameof(typeOrClass));
return Runtime.PyObject_TypeCheck(obj, typeOrClass.obj);
}
internal PyType PyType => this.GetPythonType();
///
/// 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(Reference, 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(Reference, name.Reference) != 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));
using var op = Runtime.PyObject_GetAttrString(obj, name);
return new PyObject(op.StealOrThrow());
}
///
/// Returns the named attribute of the Python object, or the given
/// default object if the attribute access throws AttributeError.
///
///
/// This method ignores any AttrubiteError(s), even ones
/// not raised due to missing requested attribute.
///
/// For example, if attribute getter calls other Python code, and
/// that code happens to cause AttributeError elsewhere, it will be ignored
/// and value will be returned instead.
///
/// Name of the attribute.
/// The object to return on AttributeError.
[Obsolete("See remarks")]
public PyObject GetAttr(string name, PyObject _default)
{
if (name == null) throw new ArgumentNullException(nameof(name));
using var op = Runtime.PyObject_GetAttrString(obj, name);
if (op.IsNull())
{
if (Exceptions.ExceptionMatches(Exceptions.AttributeError))
{
Runtime.PyErr_Clear();
return _default;
}
else
{
throw PythonException.ThrowLastAsClrException();
}
}
return new PyObject(op.Steal());
}
///
/// 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));
using var op = Runtime.PyObject_GetAttr(obj, name.obj);
return new PyObject(op.StealOrThrow());
}
///
/// Returns the named attribute of the Python object, or the given
/// default object if the attribute access throws AttributeError.
///
///
/// This method ignores any AttrubiteError(s), even ones
/// not raised due to missing requested attribute.
///
/// For example, if attribute getter calls other Python code, and
/// that code happens to cause AttributeError elsewhere, it will be ignored
/// and value will be returned instead.
///
/// Name of the attribute. Must be of Python type 'str'.
/// The object to return on AttributeError.
[Obsolete("See remarks")]
public PyObject GetAttr(PyObject name, PyObject _default)
{
if (name == null) throw new ArgumentNullException(nameof(name));
using var op = Runtime.PyObject_GetAttr(obj, name.obj);
if (op.IsNull())
{
if (Exceptions.ExceptionMatches(Exceptions.AttributeError))
{
Runtime.PyErr_Clear();
return _default;
}
else
{
throw PythonException.ThrowLastAsClrException();
}
}
return new PyObject(op.Steal());
}
///
/// 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 PythonException.ThrowLastAsClrException();
}
}
///
/// 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 PythonException.ThrowLastAsClrException();
}
}
///
/// 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_DelAttrString(obj, name);
if (r < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
///
/// 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_DelAttr(obj, name.obj);
if (r < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
///
/// 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));
using var op = Runtime.PyObject_GetItem(obj, key.obj);
if (op.IsNull())
{
throw PythonException.ThrowLastAsClrException();
}
return new PyObject(op.Steal());
}
///
/// 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 PythonException.ThrowLastAsClrException();
}
}
///
/// 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 PythonException.ThrowLastAsClrException();
}
}
///
/// 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);
}
///
/// Returns the length for objects that support the Python sequence
/// protocol.
///
public virtual long Length()
{
var s = Runtime.PyObject_Size(Reference);
if (s < 0)
{
throw PythonException.ThrowLastAsClrException();
}
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); }
}
///
/// Return a new (Python) iterator for the object. This is equivalent
/// to the Python expression "iter(object)".
///
/// Thrown if the object can not be iterated.
public PyIter GetIterator() => 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);
using var r = Runtime.PyObject_Call(obj, t.obj, null);
t.Dispose();
return new PyObject(r.StealOrThrow());
}
///
/// 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));
using var r = Runtime.PyObject_Call(obj, args.obj, null);
return new PyObject(r.StealOrThrow());
}
///
/// 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();
using var t = new PyTuple(args);
using var r = Runtime.PyObject_Call(obj, t.obj, kw is null ? null : kw.obj);
return new PyObject(r.StealOrThrow());
}
///
/// 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));
using var r = Runtime.PyObject_Call(obj, args.obj, kw is null ? null : kw.obj);
return new PyObject(r.StealOrThrow());
}
///
/// 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;
}
///
/// 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));
return IsSubclass(typeOrClass.Reference);
}
internal bool IsSubclass(BorrowedReference typeOrClass)
{
if (typeOrClass.IsNull) throw new ArgumentNullException(nameof(typeOrClass));
int r = Runtime.PyObject_IsSubclass(Reference, typeOrClass);
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()
{
using var r = Runtime.PyObject_Dir(obj);
return new PyList(r.StealOrThrow());
}
///
/// Repr Method
///
///
/// Return a string representation of the object. This method is
/// the managed equivalent of the Python expression "repr(object)".
///
public string? Repr()
{
using var strval = Runtime.PyObject_Repr(obj);
return Runtime.GetManagedString(strval.BorrowOrThrow());
}
///
/// 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()
{
using var _ = Py.GIL();
using var strval = Runtime.PyObject_Str(obj);
return Runtime.GetManagedString(strval.BorrowOrThrow());
}
ManagedType? InternalManagedObject => ManagedType.GetManagedObject(this.Reference);
string? DebuggerDisplay
{
get
{
if (DebugUtil.HaveInterpreterLock())
return this.ToString();
var obj = this.InternalManagedObject;
return obj is { }
? obj.ToString()
: $"pyobj at 0x{this.rawPtr:X} (get Py.GIL to see more info)";
}
}
///
/// 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)
{
using var _ = Py.GIL();
return Equals(o as PyObject);
}
public virtual bool Equals(PyObject? other)
{
if (other is null) return false;
if (obj == other.obj)
{
return true;
}
int result = Runtime.PyObject_RichCompareBool(obj, other.obj, Runtime.Py_EQ);
if (result < 0) throw PythonException.ThrowLastAsClrException();
return result != 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()
{
using var _ = Py.GIL();
nint pyHash = Runtime.PyObject_Hash(obj);
if (pyHash == -1 && Exceptions.ErrorOccurred())
{
throw PythonException.ThrowLastAsClrException();
}
return pyHash.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)
{
CheckDisposed();
return new PyBuffer(this, flags);
}
public long Refcount
{
get
{
return Runtime.Refcount(obj);
}
}
internal int CompareTo(BorrowedReference other)
{
int greater = Runtime.PyObject_RichCompareBool(this.Reference, other, Runtime.Py_GT);
Debug.Assert(greater != -1);
if (greater > 0)
return 1;
int less = Runtime.PyObject_RichCompareBool(this.Reference, other, Runtime.Py_LT);
Debug.Assert(less != -1);
return less > 0 ? -1 : 0;
}
internal bool Equals(BorrowedReference other)
{
int equal = Runtime.PyObject_RichCompareBool(this.Reference, other, Runtime.Py_EQ);
Debug.Assert(equal != -1);
return equal > 0;
}
public override bool TryGetMember(GetMemberBinder binder, out object? result)
{
using var _ = Py.GIL();
result = CheckNone(this.GetAttr(binder.Name));
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object? value)
{
using var _ = Py.GIL();
using var newVal = Converter.ToPythonDetectType(value);
int r = Runtime.PyObject_SetAttrString(obj, binder.Name, newVal.Borrow());
if (r < 0)
{
throw PythonException.ThrowLastAsClrException();
}
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;
using var argTuple = Runtime.PyTuple_New(regularArgumentCount);
for (int i = 0; i < regularArgumentCount; ++i)
{
AddArgument(argTuple.Borrow(), i, inargs[i]);
}
args = new PyTuple(argTuple.Steal());
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)
{
;
}
using var argtuple = Runtime.PyTuple_New(arg_count);
for (var i = 0; i < arg_count; i++)
{
AddArgument(argtuple.Borrow(), i, inargs[i]);
}
args = new PyTuple(argtuple.Steal());
kwargs = null;
for (int i = arg_count; i < inargs.Length; i++)
{
if (inargs[i] is not Py.KeywordArguments kw)
{
throw new ArgumentException("Keyword arguments must come after normal arguments.");
}
if (kwargs == null)
{
kwargs = kw;
}
else
{
kwargs.Update(kw);
}
}
}
private static void AddArgument(BorrowedReference argtuple, nint i, object? target)
{
using var ptr = GetPythonObject(target);
if (Runtime.PyTuple_SetItem(argtuple, i, ptr.StealNullable()) < 0)
{
throw PythonException.ThrowLastAsClrException();
}
}
private static NewReference GetPythonObject(object? target)
{
if (target is PyObject pyObject)
{
return new NewReference(pyObject);
}
else
{
return Converter.ToPythonDetectType(target);
}
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object?[] args, out object? result)
{
using var _ = Py.GIL();
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
{
pyargs?.Dispose();
kwargs?.Dispose();
}
return true;
}
else
{
return base.TryInvokeMember(binder, args, out result);
}
}
public override bool TryInvoke(InvokeBinder binder, object?[] args, out object? result)
{
using var _ = Py.GIL();
if (this.IsCallable())
{
PyTuple? pyargs = null;
PyDict? kwargs = null;
try
{
GetArgs(args, binder.CallInfo, out pyargs, out kwargs);
result = CheckNone(Invoke(pyargs, kwargs));
}
finally
{
pyargs?.Dispose();
kwargs?.Dispose();
}
return true;
}
else
{
return base.TryInvoke(binder, args, out result);
}
}
public override bool TryConvert(ConvertBinder binder, out object? result)
{
using var _ = Py.GIL();
// always try implicit conversion first
if (Converter.ToManaged(this.obj, binder.Type, out result, false))
{
return true;
}
if (binder.Explicit)
{
Runtime.PyErr_Fetch(out var errType, out var errValue, out var tb);
bool converted = Converter.ToManagedExplicit(Reference, binder.Type, out result);
Runtime.PyErr_Restore(errType.StealNullable(), errValue.StealNullable(), tb.StealNullable());
return converted;
}
if (binder.Type == typeof(System.Collections.IEnumerable) && this.IsIterable())
{
result = new PyIterable(this.Reference);
return true;
}
return false;
}
private bool TryCompare(PyObject arg, int op, out object @out)
{
int result = Runtime.PyObject_RichCompareBool(this.obj, arg.obj, op);
@out = result != 0;
if (result < 0)
{
Exceptions.Clear();
return false;
}
return true;
}
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object? result)
{
using var _ = Py.GIL();
NewReference res;
if (arg is not 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:
return this.TryCompare((PyObject)arg, Runtime.Py_GT, out result);
case ExpressionType.GreaterThanOrEqual:
return this.TryCompare((PyObject)arg, Runtime.Py_GE, out result);
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:
return this.TryCompare((PyObject)arg, Runtime.Py_LT, out result);
case ExpressionType.LessThanOrEqual:
return this.TryCompare((PyObject)arg, Runtime.Py_LE, out result);
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:
return this.TryCompare((PyObject)arg, Runtime.Py_NE, out result);
case ExpressionType.Equal:
return this.TryCompare((PyObject)arg, Runtime.Py_EQ, out result);
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;
}
Exceptions.ErrorCheck(res.BorrowNullable());
result = CheckNone(new PyObject(res.Borrow()));
return true;
}
public static bool operator ==(PyObject? a, PyObject? b)
{
if (a is null && b is null)
{
return true;
}
if (a is null || b is null)
{
return false;
}
using var _ = Py.GIL();
int result = Runtime.PyObject_RichCompareBool(a.obj, b.obj, Runtime.Py_EQ);
if (result < 0) throw PythonException.ThrowLastAsClrException();
return result != 0;
}
public static bool operator !=(PyObject? a, PyObject? b)
{
if (a is null && b is null)
{
return false;
}
if (a is null || b is null)
{
return true;
}
using var _ = Py.GIL();
int result = Runtime.PyObject_RichCompareBool(a.obj, b.obj, Runtime.Py_NE);
if (result < 0) throw PythonException.ThrowLastAsClrException();
return result != 0;
}
// Workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=41509
// See https://github.com/pythonnet/pythonnet/pull/219
internal 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)
{
using var _ = Py.GIL();
int r;
NewReference 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;
if (r == -1) Exceptions.Clear();
return r != -1;
case ExpressionType.IsFalse:
r = Runtime.PyObject_IsTrue(this.obj);
result = r == 0;
if (r == -1) Exceptions.Clear();
return r != -1;
case ExpressionType.IsTrue:
r = Runtime.PyObject_IsTrue(this.obj);
result = r == 1;
if (r == -1) Exceptions.Clear();
return r != -1;
case ExpressionType.Decrement:
case ExpressionType.Increment:
default:
result = null;
return false;
}
result = CheckNone(new PyObject(res.StealOrThrow()));
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()
{
using var _ = Py.GIL();
return Dir().Select(pyObj => pyObj.ToString()!).ToArray();
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
=> GetObjectData(info, context);
protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
#pragma warning disable CS0618 // Type or member is obsolete
Runtime.XIncref(this);
#pragma warning restore CS0618 // Type or member is obsolete
info.AddValue("h", rawPtr.ToInt64());
info.AddValue("r", run);
}
protected PyObject(SerializationInfo info, StreamingContext context)
{
rawPtr = (IntPtr)info.GetInt64("h");
run = info.GetInt32("r");
if (IsDisposed) GC.SuppressFinalize(this);
}
}
internal static class PyObjectExtensions
{
internal static NewReference NewReferenceOrNull(this PyObject? self)
=> self is null || self.IsDisposed ? default : new NewReference(self);
internal static BorrowedReference BorrowNullable(this PyObject? self)
=> self is null ? default : self.Reference;
}
}