lib/pycall/pyobject_wrapper.rb in pycall-0.1.0.alpha.20170307 vs lib/pycall/pyobject_wrapper.rb in pycall-0.1.0.alpha.20170308

- old
+ new

@@ -1,12 +1,29 @@ module PyCall + Py_LT = 0 + Py_LE = 1 + Py_EQ = 2 + Py_NE = 3 + Py_GT = 4 + Py_GE = 5 + + RICH_COMPARISON_OPCODES = { + :< => Py_LT, + :<= => Py_LE, + :== => Py_EQ, + :!= => Py_NE, + :> => Py_GT, + :>= => Py_GE + }.freeze + module PyObjectWrapper module ClassMethods private - def wrap_class(pyobj) - define_singleton_method(:__pyobj__) { pyobj } + def wrap_class(pyclass) + pyclass__pyobj__ = pyclass.__pyobj__ + define_singleton_method(:__pyobj__) { pyclass__pyobj__ } PyCall.dir(__pyobj__).each do |name| obj = PyCall.getattr(__pyobj__, name) next unless obj.kind_of?(PyCall::PyObject) || obj.kind_of?(PyCall::PyObjectWrapper) next unless PyCall.callable?(obj) @@ -29,31 +46,127 @@ def self.included(mod) mod.extend ClassMethods end - def initialize(pyobj, pytype=nil) - check_type pyobj, pytype - pytype ||= LibPython.PyObject_Type(pyobj) + def initialize(pyobj) + pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct @__pyobj__ = pyobj end attr_reader :__pyobj__ - def ==(other) - case other - when self.class - __pyobj__ == other.__pyobj__ - when PyObject - __pyobj__ == other + def type + LibPython.PyObject_Type(__pyobj__).to_ruby + end + + def null? + __pyobj__.null? + end + + def to_ptr + __pyobj__.to_ptr + end + + def py_none? + to_ptr == PyCall.None.to_ptr + end + + def kind_of?(klass) + case klass + when PyObjectWrapper + __pyobj__.kind_of? klass.__pyobj__ + when LibPython::PyObjectStruct + __pyobj__.kind_of? klass else super end end + def rich_compare(other, op) + opcode = RICH_COMPARISON_OPCODES[op] + raise ArgumentError, "Unknown comparison op: #{op}" unless opcode + + other = other.__pyobj__ unless other.kind_of? LibPython::PyObjectStruct + other = Conversions.from_ruby(other) unless other.kind_of?(LibPython::PyObjectStruct) + return other.null? if __pyobj__.null? + return false if other.null? + + value = LibPython.PyObject_RichCompare(__pyobj__, other, opcode) + raise "Unable to compare: #{self} #{op} #{other}" if value.null? + value.to_ruby + end + + RICH_COMPARISON_OPCODES.keys.each do |op| + define_method(op) {|other| rich_compare(other, op) } + end + + def [](*indices) + if indices.length == 1 + indices = indices[0] + else + indices = PyCall.tuple(*indices) + end + PyCall.getitem(self, indices) + end + + def []=(*indices_and_value) + value = indices_and_value.pop + indices = indices_and_value + if indices.length == 1 + indices = indices[0] + else + indices = PyCall.tuple(*indices) + end + PyCall.setitem(self, indices, value) + end + + def +(other) + other = Conversions.from_ruby(other) + value = LibPython.PyNumber_Add(self, other) + return value.to_ruby unless value.null? + raise PyError.fetch + end + + def -(other) + other = Conversions.from_ruby(other) + value = LibPython.PyNumber_Subtract(self, other) + return value.to_ruby unless value.null? + raise PyError.fetch + end + + def *(other) + other = Conversions.from_ruby(other) + value = LibPython.PyNumber_Multiply(self, other) + return value.to_ruby unless value.null? + raise PyError.fetch + end + + def /(other) + other = Conversions.from_ruby(other) + value = LibPython.PyNumber_TrueDivide(self, other) + return value.to_ruby unless value.null? + raise PyError.fetch + end + + def **(other) + other = Conversions.from_ruby(other) + value = LibPython.PyNumber_Power(self, other, PyCall.None) + return value.to_ruby unless value.null? + raise PyError.fetch + end + + def coerce(other) + [Conversions.from_ruby(other), self] + end + def call(*args, **kwargs) - __pyobj__.call(*args, **kwargs) + args = PyCall::Tuple[*args] + kwargs = kwargs.empty? ? PyObject.null : PyCall::Dict.new(kwargs) + res = LibPython.PyObject_Call(__pyobj__, args.__pyobj__, kwargs.__pyobj__) + return res.to_ruby if LibPython.PyErr_Occurred().null? + raise PyError.fetch end def method_missing(name, *args, **kwargs) if PyCall.hasattr?(__pyobj__, name.to_s) PyCall.getattr(__pyobj__, name) @@ -61,21 +174,20 @@ super end end def to_s - __pyobj__.to_s + s = LibPython.PyObject_Repr(__pyobj__) + if s.null? + LibPython.PyErr_Clear() + s = LibPython.PyObject_Str(__pyobj__) + if s.null? + LibPython.PyErr_Clear() + return super + end + end + s.to_ruby end - def inspect - __pyobj__.inspect - end - - private - - def check_type(pyobj, pytype) - return if pyobj.kind_of?(PyObject) - return if pytype.nil? || pyobj.kind_of?(pytype) - raise TypeError, "the argument must be a PyObject of #{pytype}" - end + alias inspect to_s end end