// ========================================================================== // This software is subject to the provisions of the Zope Public License, // Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. // THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED // WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS // FOR A PARTICULAR PURPOSE. // ========================================================================== using System; namespace Python.Runtime { /// /// Represents a generic Python object. The methods of this class are /// generally equivalent to the Python "abstract object API". See /// http://www.python.org/doc/current/api/object.html for details. /// public class PyObject : IDisposable { protected internal IntPtr obj = IntPtr.Zero; private bool disposed = false; /// /// 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) { obj = ptr; } // Protected default constructor to allow subclasses to manage // initialization in different ways as appropriate. protected PyObject() {} // Ensure that encapsulated Python object is decref'ed appropriately // when the managed wrapper is garbage-collected. ~PyObject() { Dispose(); } /// /// 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; } } /// /// FromManagedObject Method /// /// /// /// 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.Incref(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(this.Handle, t, out result, false)) { throw new InvalidCastException("cannot convert object to target type"); } return 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. /// public void Dispose() { if (!disposed) { if (Runtime.Py_IsInitialized() > 0) { IntPtr gs = PythonEngine.AcquireLock(); Runtime.Decref(obj); obj = IntPtr.Zero; PythonEngine.ReleaseLock(gs); } GC.SuppressFinalize(this); disposed = true; } } /// /// 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) { 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) { 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) { 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) { IntPtr op = Runtime.PyObject_GetAttrString(obj, name); 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. /// public PyObject GetAttr(string name, PyObject _default) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { 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) { return GetItem(new PyString(key)); } /// /// 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) { PyInt key = new PyInt(index); return GetItem((PyObject)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) { 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) { SetItem(new PyString(key), 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) { SetItem(new PyInt(index), 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) { 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) { DelItem(new PyString(key)); } /// /// 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) { DelItem(new PyInt(index)); } /// /// 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 int Length() { int 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); } /// /// Invoke Method /// /// /// /// Invoke the callable object with the given arguments, passed as a /// PyObject[]. A PythonException is raised if the invokation fails. /// public PyObject Invoke(params PyObject[] args) { PyTuple 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 invokation fails. /// public PyObject Invoke(PyTuple 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 invokation fails. /// public PyObject Invoke(PyObject[] args, PyDict kw) { PyTuple t = new PyTuple(args); IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw.obj); 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 invokation fails. /// public PyObject Invoke(PyTuple args, PyDict kw) { IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw.obj); 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 invokation is unsuccessful. /// public PyObject InvokeMethod(string name, params PyObject[] 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 invokation is unsuccessful. /// public PyObject InvokeMethod(string name, PyTuple 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 invokation is unsuccessful. /// public PyObject InvokeMethod(string name, PyObject[] args, PyDict kw) { 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 invokation is unsuccessful. /// public PyObject InvokeMethod(string name, PyTuple args, PyDict kw) { 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) { 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) { 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); } /// /// 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); } /// /// 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.Decref(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.Decref(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 Runtime.PyObject_Hash(obj).ToInt32(); } } }