1. raineszm
  2. rubypython

Commits

raineszm  committed bfd8907

Added basic support for callbacks. Inspired by RuPy by Steeve Morin. Just testing this out for now.

  • Participants
  • Parent commits 9d0ced2
  • Branches default

Comments (0)

Files changed (7)

File lib/rubypython/conversion.rb

View file
         rtopSymbol rObj
       when nil
         rtopNone
+      when Proc
+        rtopProc rObj
       else
         raise UnsupportedConversion.new("Unsupported type for RTOP conversion." )
       end
       rb_hash
     end
 
+    def self.rtopProc(rObj)
+      pyMethodDef = Python::PyMethodDef.new
+      callback = Proc.new do |py_self, py_args|
+        rObj.call(RubyPyProxy.new(py_args).to_a).pObject.pointer
+      end
+      pyMethodDef[:ml_name] = FFI::MemoryPointer.from_string "Proc::#{rObj.object_id}"
+      pyMethodDef[:ml_meth] = callback
+      pyMethodDef[:ml_flags] = Python::METH_VARARGS
+      pyMethodDef[:ml_doc] = nil
+
+      Python::PyCFunction_New pyMethodDef, nil
+    end
+
 
     #Converts a pointer to a Python object into a native ruby type, if
     #possible. Otherwise raises an error.

File lib/rubypython/python.rb

View file
     attach_function :PyDict_Contains, [:pointer, :pointer], :int
     attach_function :PyDict_GetItem, [:pointer, :pointer], :pointer
 
+    #Function Constants
+    METH_VARARGS = 1
+    attach_function :PyCFunction_New, [:pointer, :pointer], :pointer
+    callback :PyCFunction, [:pointer, :pointer], :pointer
+
     #Error Methods
     attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void
     attach_function :PyErr_Occurred, [], :pointer
         :ob_type, :pointer
     end
 
+    #This struct is used when defining Python methods. 
+    class PyMethodDef < FFI::Struct
+      layout :ml_name, :pointer,
+        :ml_meth, :PyCFunction,
+        :ml_flags, :int,
+        :ml_doc, :pointer
+    end
+
   end
 end

File spec/callback_spec.rb

View file
+require File.dirname(__FILE__) + '/spec_helper.rb'
+
+describe 'Callbacks' do
+  include TestConstants
+  
+  before do
+    RubyPython.start
+    @sys = RubyPython.import 'sys'
+    @sys.path.append './spec/python_helpers'
+    @objects = RubyPython.import 'objects'
+  end
+
+  after do
+    RubyPython.start
+  end
+
+  it "procs should be accepted as functions" do
+    [
+      [2, 2],
+      ["a", "Word"],
+      [ [1, 2], [3, 4] ]
+    ].each do |args|
+      @objects.apply_callback(AProc, args).should == AProc.call(*args)
+    end
+  end
+
+end

File spec/conversion_spec.rb

View file
       ["a dict", "a hash", AConvertedHash],
       ["python True", "true", true],
       ["python False", "false", false],
-      ["python None", "nil", nil]
-    ].each do |py_type, rb_type, input|
+      ["python None", "nil", nil],
+      ["a function", "a proc", AProc, true]
+    ].each do |py_type, rb_type, input, no_compare|
 
       it "should convert #{rb_type} to #{py_type}" do
         py_object_ptr = subject.rtopObject(input)
-        output = @objects.__send__(rb_type.sub(' ', '_')).pObject.pointer
-        RubyPython::Python.PyObject_Compare(py_object_ptr, output).should == 0
+        unless no_compare
+          output = @objects.__send__(rb_type.sub(' ', '_')).pObject.pointer
+          RubyPython::Python.PyObject_Compare(py_object_ptr, output).should == 0
+        end
       end
     end
 
 
   end
 
+
 end

File spec/python_helpers/objects.py

View file
 def identity(object):
     return object
 
+def apply_callback(callback, args):
+    return callback(*args)
+
 class RubyPythonMockObject:
     STRING = "STRING"
     STRING_LIST = ["STRING1", "STRING2"]

File spec/rubypyproxy_spec.rb

View file
       urllib2 = RubyPython.import('urllib2')
       urllib2.Request.should be_a(RubyPython::RubyPyClass)
     end
+
   end
 
   describe "when used with an operator" do

File spec/spec_helper.rb

View file
       key = k.is_a?(Symbol)? k.to_s : k
       [key,v]
     end.flatten]
+
+    AProc = Proc.new { |a1, a2| a1 + a2 }
+
 end
 
 def run_python_command(cmd)