lib/pycall/pyobject_wrapper.rb in pycall-0.1.0.alpha.20170711 vs lib/pycall/pyobject_wrapper.rb in pycall-1.0.0
- old
+ new
@@ -1,213 +1,212 @@
-module PyCall
- HASH_SALT = "PyCall::PyObject".hash
+require 'pycall/wrapper_object_cache'
- 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 PyCall
module PyObjectWrapper
- module ClassMethods
- private
+ attr_reader :__pyptr__
- def wrap_class(pyclass)
- pyclass__pyobj__ = pyclass.__pyobj__
- define_singleton_method(:__pyobj__) { pyclass__pyobj__ }
+ def self.extend_object(obj)
+ pyptr = obj.instance_variable_get(:@__pyptr__)
+ unless pyptr.kind_of? PyPtr
+ raise TypeError, "@__pyptr__ should have PyCall::PyPtr object"
+ end
+ super
+ end
- 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)
+ OPERATOR_METHOD_NAMES = {
+ :+ => :__add__,
+ :- => :__sub__,
+ :* => :__mul__,
+ :/ => :__truediv__,
+ :% => :__mod__,
+ :** => :__pow__,
+ :<< => :__lshift__,
+ :>> => :__rshift__,
+ :& => :__and__,
+ :^ => :__xor__,
+ :| => :__or__
+ }.freeze
- define_method(name) do |*args, **kwargs|
- PyCall.getattr(__pyobj__, name).(*args, **kwargs)
- end
+ def method_missing(name, *args)
+ name_str = name.to_s if name.kind_of?(Symbol)
+ name_str.chop! if name_str.end_with?('=')
+ case name
+ when *OPERATOR_METHOD_NAMES.keys
+ op_name = OPERATOR_METHOD_NAMES[name]
+ if LibPython::Helpers.hasattr?(__pyptr__, op_name)
+ LibPython::Helpers.define_wrapper_method(self, op_name)
+ singleton_class.__send__(:alias_method, name, op_name)
+ return self.__send__(name, *args)
end
-
- class << self
- def method_missing(name, *args, **kwargs)
- return super unless PyCall.hasattr?(__pyobj__, name)
- PyCall.getattr(__pyobj__, name)
- end
+ else
+ if LibPython::Helpers.hasattr?(__pyptr__, name_str)
+ LibPython::Helpers.define_wrapper_method(self, name)
+ return self.__send__(name, *args)
end
-
- PyCall::Conversions.python_type_mapping(__pyobj__, self)
end
+ super
end
- def self.included(mod)
- mod.extend ClassMethods
+ def respond_to_missing?(name, include_private)
+ return true if LibPython::Helpers.hasattr?(__pyptr__, name)
+ super
end
- def initialize(pyobj)
- pyobj = LibPython::PyObjectStruct.new(pyobj) if pyobj.kind_of? FFI::Pointer
- pyobj = pyobj.__pyobj__ unless pyobj.kind_of? LibPython::PyObjectStruct
- @__pyobj__ = pyobj
+ def kind_of?(cls)
+ case cls
+ when PyTypeObjectWrapper
+ __pyptr__.kind_of?(cls.__pyptr__)
+ else
+ super
+ end
end
- attr_reader :__pyobj__
-
- def eql?(other)
- rich_compare(other, :==)
+ [:==, :!=, :<, :<=, :>, :>=].each do |op|
+ class_eval("#{<<-"begin;"}\n#{<<-"end;"}", __FILE__, __LINE__+1)
+ begin;
+ def #{op}(other)
+ case other
+ when PyObjectWrapper
+ LibPython::Helpers.compare(:#{op}, __pyptr__, other.__pyptr__)
+ else
+ other = Conversion.from_ruby(other)
+ LibPython::Helpers.compare(:#{op}, __pyptr__, other)
+ end
+ end
+ end;
end
- def hash
- hash_value = LibPython.PyObject_Hash(__pyobj__)
- return super if hash_value == -1
- hash_value
+ def [](*key)
+ LibPython::Helpers.getitem(__pyptr__, key)
end
- def type
- LibPython.PyObject_Type(__pyobj__).to_ruby
+ def []=(*key, value)
+ LibPython::Helpers.setitem(__pyptr__, key, value)
end
- def null?
- __pyobj__.null?
+ def call(*args)
+ LibPython::Helpers.call_object(__pyptr__, *args)
end
- def to_ptr
- __pyobj__.to_ptr
- end
+ class SwappedOperationAdapter
+ def initialize(obj)
+ @obj = obj
+ end
- def py_none?
- to_ptr == PyCall.None.to_ptr
- end
+ attr_reader :obj
- def kind_of?(klass)
- case klass
- when PyObjectWrapper
- __pyobj__.kind_of? klass.__pyobj__
- when LibPython::PyObjectStruct
- __pyobj__.kind_of? klass
- else
- super
+ def +(other)
+ other.__radd__(self.obj)
end
- end
- def rich_compare(other, op)
- opcode = RICH_COMPARISON_OPCODES[op]
- raise ArgumentError, "Unknown comparison op: #{op}" unless opcode
+ def -(other)
+ other.__rsub__(self.obj)
+ end
- other = Conversions.from_ruby(other)
- return other.null? if __pyobj__.null?
- return false if other.null?
+ def *(other)
+ other.__rmul__(self.obj)
+ end
- value = LibPython.PyObject_RichCompare(__pyobj__, other, opcode)
- raise "Unable to compare: #{self} #{op} #{other}" if value.null?
- value.to_ruby
- end
+ def /(other)
+ other.__rtruediv__(self.obj)
+ end
- RICH_COMPARISON_OPCODES.keys.each do |op|
- define_method(op) {|other| rich_compare(other, op) }
- end
+ def %(other)
+ other.__rmod__(self.obj)
+ end
- def [](*indices)
- if indices.length == 1
- indices = indices[0]
- else
- indices = PyCall.tuple(*indices)
+ def **(other)
+ other.__rpow__(self.obj)
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)
+ def <<(other)
+ other.__rlshift__(self.obj)
end
- PyCall.setitem(self, indices, value)
+
+ def >>(other)
+ other.__rrshift__(self.obj)
+ end
+
+ def &(other)
+ other.__rand__(self.obj)
+ end
+
+ def ^(other)
+ other.__rxor__(self.obj)
+ end
+
+ def |(other)
+ other.__ror__(self.obj)
+ end
end
- def +(other)
- other = Conversions.from_ruby(other)
- value = LibPython.PyNumber_Add(__pyobj__, other)
- return value.to_ruby unless value.null?
- raise PyError.fetch
+ def coerce(other)
+ [SwappedOperationAdapter.new(other), self]
end
- def -(other)
- other = Conversions.from_ruby(other)
- value = LibPython.PyNumber_Subtract(__pyobj__, other)
- return value.to_ruby unless value.null?
- raise PyError.fetch
+ def dup
+ super.tap do |duped|
+ copied = PyCall.import_module('copy').copy(__pyptr__)
+ copied = copied.__pyptr__ if copied.kind_of? PyObjectWrapper
+ duped.instance_variable_set(:@__pyptr__, copied)
+ end
end
- def *(other)
- other = Conversions.from_ruby(other)
- value = LibPython.PyNumber_Multiply(__pyobj__, other)
- return value.to_ruby unless value.null?
- raise PyError.fetch
+ def inspect
+ PyCall.builtins.repr(__pyptr__)
end
- def /(other)
- other = Conversions.from_ruby(other)
- value = LibPython.PyNumber_TrueDivide(__pyobj__, other)
- return value.to_ruby unless value.null?
- raise PyError.fetch
+ def to_s
+ LibPython::Helpers.str(__pyptr__)
end
- def **(other)
- other = Conversions.from_ruby(other)
- value = LibPython.PyNumber_Power(__pyobj__, other, PyCall.None)
- return value.to_ruby unless value.null?
- raise PyError.fetch
+ def to_i
+ LibPython::Helpers.call_object(PyCall::builtins.int.__pyptr__, __pyptr__)
end
- def coerce(other)
- [PyObject.new(Conversions.from_ruby(other)), self]
+ def to_f
+ LibPython::Helpers.call_object(PyCall::builtins.float.__pyptr__, __pyptr__)
end
+ end
- def 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
+ module_function
+
+ class WrapperModuleCache < WrapperObjectCache
+ def initialize
+ super(LibPython::API::PyModule_Type)
end
- def method_missing(name, *args, **kwargs)
- name_s = name.to_s
- if name_s.end_with? '='
- name = name_s[0..-2]
- if PyCall.hasattr?(__pyobj__, name.to_s)
- PyCall.setattr(__pyobj__, name, args.first)
- else
- raise NameError, "object has no attribute `#{name}'"
- end
- elsif PyCall.hasattr?(__pyobj__, name.to_s)
- PyCall.getattr(__pyobj__, name)
- else
- super
+ def check_wrapper_object(wrapper_object)
+ unless wrapper_object.kind_of?(Module) && wrapper_object.kind_of?(PyObjectWrapper)
+ raise TypeError, "unexpected type #{wrapper_object.class} (expected Module extended by PyObjectWrapper)"
end
end
- def 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
+ def self.instance
+ @instance ||= self.new
+ end
+ end
+
+ private_constant :WrapperModuleCache
+
+ def wrap_module(pymodptr)
+ check_ismodule(pymodptr)
+ WrapperModuleCache.instance.lookup(pymodptr) do
+ Module.new do |mod|
+ mod.instance_variable_set(:@__pyptr__, pymodptr)
+ mod.extend PyObjectWrapper
end
- s.to_ruby
end
+ end
- alias inspect to_s
+ def check_isclass(pyptr)
+ pyptr = pyptr.__pyptr__ if pyptr.kind_of? PyObjectWrapper
+ return if pyptr.kind_of? LibPython::API::PyType_Type
+ return defined?(LibPython::API::PyClass_Type) && pyptr.kind_of?(LibPython::API::PyClass_Type)
+ raise TypeError, "PyType object is required"
+ end
+
+ def check_ismodule(pyptr)
+ return if pyptr.kind_of? LibPython::API::PyModule_Type
+ raise TypeError, "PyModule object is required"
end
end