rubypython 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,162 +1,185 @@
1
1
  require 'ffi'
2
2
  require 'open3'
3
+ require 'rubypython/options'
3
4
 
4
5
  module RubyPython
5
- #This module provides access to the Python C API functions via the Ruby ffi
6
- #gem. Documentation for these functions may be found [here](http://docs.python.org/c-api/). Likewise the FFI gem documentation may be found [here](http://rdoc.info/projects/ffi/ffi).
7
- module Python
8
- extend FFI::Library
9
-
10
- # This much we can assume works without anything special at all.
11
- PYTHON_VERSION = Open3.popen3("python --version") { |i,o,e| e.read }.chomp.split[1].to_f
12
- PYTHON_NAME = "python#{PYTHON_VERSION}"
13
- LIB_NAME = "#{FFI::Platform::LIBPREFIX}#{PYTHON_NAME}"
14
- LIB_EXT = FFI::Platform::LIBSUFFIX
15
- PYTHON_SYS_PREFIX = %x{#{PYTHON_NAME} -c "import sys; print(sys.prefix)"}.chomp
16
-
17
- # Here's where we run into problems, as not everything works quite the
18
- # way we expect it to.
19
- #
20
- # The default libname will be something like libpython2.6.so (or .dylib)
21
- # or maybe even python2.6.dll on Windows.
22
- libname = "#{LIB_NAME}.#{LIB_EXT}"
23
-
24
- # We may need to look in multiple locations for Python, so let's build
25
- # this as an array.
26
- locations = [ File.join(PYTHON_SYS_PREFIX, "lib", libname) ]
27
-
28
- if FFI::Platform.mac?
29
- # On the Mac, let's add a special case that has even a different
30
- # libname. This may not be fully useful on future versions of OS X,
31
- # but it should work on 10.5 and 10.6. Even if it doesn't, the next
32
- # step will (/usr/lib/libpython<version>.dylib is a symlink to the
33
- # correct location).
34
- locations << File.join(PYTHON_SYS_PREFIX, "Python")
35
- # Let's also look in the location that was originally set in this
36
- # library:
37
- File.join(PYTHON_SYS_PREFIX, "lib", "#{PYTHON_NAME}", "config",
38
- libname)
39
- end
40
-
41
- if FFI::Platform.unix?
42
- # On Unixes, let's look in alternative places, too. Just in case.
43
- locations << File.join("/opt/local/lib", libname)
44
- locations << File.join("/opt/lib", libname)
45
- locations << File.join("/usr/local/lib", libname)
46
- locations << File.join("/usr/lib", libname)
47
- end
48
-
49
- # Get rid of redundant locations.
50
- locations.uniq!
51
-
52
- dyld_flags = FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL
53
- exceptions = []
54
-
55
- locations.each do |location|
56
- begin
57
- @ffi_libs = [ FFI::DynamicLibrary.open(location, dyld_flags) ]
58
- LIB = location
59
- break
60
- rescue LoadError => ex
61
- @ffi_libs = nil
62
- exceptions << ex
63
- end
64
- end
65
-
66
- raise exceptions.first if @ffi_libs.nil?
67
-
68
- #The class is a little bit of a hack to extract the address of global
69
- #structs. If someone knows a better way please let me know.
70
- class DummyStruct < FFI::Struct
71
- layout :dummy_var, :int
72
- end
6
+ #Check to see whether the Python Library has been loaded yet. If not
7
+ #then we dfer loading the library until this file has been reloaded.
8
+ if @loaded
9
+ #This module provides access to the Python C API functions via the Ruby ffi
10
+ #gem. Documentation for these functions may be found [here](http://docs.python.org/c-api/). Likewise the FFI gem documentation may be found [here](http://rdoc.info/projects/ffi/ffi).
11
+ module Python
12
+ extend FFI::Library
13
+
14
+ PYTHON_EXE = RubyPython.options[:python_exe] || 'python'
15
+ # This much we can assume works without anything special at all.
16
+ PYTHON_VERSION = Open3.popen3("#{PYTHON_EXE} --version") { |i,o,e| e.read }.chomp.split[1].to_f
17
+ PYTHON_NAME = "python#{PYTHON_VERSION}"
18
+ LIB_NAME = "#{FFI::Platform::LIBPREFIX}#{PYTHON_NAME}"
19
+ LIB_EXT = FFI::Platform::LIBSUFFIX
20
+ PYTHON_SYS_PREFIX = %x{#{PYTHON_NAME} -c "import sys; print(sys.prefix)"}.chomp
21
+
22
+ # Here's where we run into problems, as not everything works quite the
23
+ # way we expect it to.
24
+ #
25
+ # The default libname will be something like libpython2.6.so (or .dylib)
26
+ # or maybe even python2.6.dll on Windows.
27
+ libname = "#{LIB_NAME}.#{LIB_EXT}"
28
+
29
+ # We may need to look in multiple locations for Python, so let's build
30
+ # this as an array.
31
+ locations = [ File.join(PYTHON_SYS_PREFIX, "lib", libname) ]
32
+
33
+ if FFI::Platform.mac?
34
+ # On the Mac, let's add a special case that has even a different
35
+ # libname. This may not be fully useful on future versions of OS X,
36
+ # but it should work on 10.5 and 10.6. Even if it doesn't, the next
37
+ # step will (/usr/lib/libpython<version>.dylib is a symlink to the
38
+ # correct location).
39
+ locations << File.join(PYTHON_SYS_PREFIX, "Python")
40
+ # Let's also look in the location that was originally set in this
41
+ # library:
42
+ File.join(PYTHON_SYS_PREFIX, "lib", "#{PYTHON_NAME}", "config",
43
+ libname)
44
+ end
45
+
46
+ if FFI::Platform.unix?
47
+ # On Unixes, let's look in alternative places, too. Just in case.
48
+ locations << File.join("/opt/local/lib", libname)
49
+ locations << File.join("/opt/lib", libname)
50
+ locations << File.join("/usr/local/lib", libname)
51
+ locations << File.join("/usr/lib", libname)
52
+ end
53
+
54
+ # Get rid of redundant locations.
55
+ locations.uniq!
56
+
57
+ dyld_flags = FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL
58
+ exceptions = []
59
+
60
+ locations.each do |location|
61
+ begin
62
+ @ffi_libs = [ FFI::DynamicLibrary.open(location, dyld_flags) ]
63
+ LIB = location
64
+ break
65
+ rescue LoadError => ex
66
+ @ffi_libs = nil
67
+ exceptions << ex
68
+ end
69
+ end
70
+
71
+ raise exceptions.first if @ffi_libs.nil?
72
+
73
+ #The class is a little bit of a hack to extract the address of global
74
+ #structs. If someone knows a better way please let me know.
75
+ class DummyStruct < FFI::Struct
76
+ layout :dummy_var, :int
77
+ end
78
+
79
+ #Python interpreter startup and shutdown
80
+ attach_function :Py_IsInitialized, [], :int
81
+ attach_function :Py_Initialize, [], :void
82
+ attach_function :Py_Finalize, [], :void
83
+
84
+ #Module methods
85
+ attach_function :PyImport_ImportModule, [:string], :pointer
86
+
87
+ #Object Methods
88
+ attach_function :PyObject_HasAttrString, [:pointer, :string], :int
89
+ attach_function :PyObject_GetAttrString, [:pointer, :string], :pointer
90
+ attach_function :PyObject_SetAttrString, [:pointer, :string, :pointer], :int
91
+
92
+ attach_function :PyObject_Compare, [:pointer, :pointer], :int
93
+
94
+ attach_function :PyObject_CallObject, [:pointer, :pointer], :pointer
95
+ attach_function :PyCallable_Check, [:pointer], :int
96
+
97
+ ###Python To Ruby Conversion
98
+ #String Methods
99
+ attach_function :PyString_AsString, [:pointer], :string
100
+ attach_function :PyString_FromString, [:string], :pointer
101
+
102
+ #List Methods
103
+ attach_function :PyList_GetItem, [:pointer, :int], :pointer
104
+ attach_function :PyList_Size, [:pointer], :int
105
+ attach_function :PyList_New, [:int], :pointer
106
+ attach_function :PyList_SetItem, [:pointer, :int, :pointer], :void
107
+
108
+ #Integer Methods
109
+ attach_function :PyInt_AsLong, [:pointer], :long
110
+ attach_function :PyInt_FromLong, [:long], :pointer
111
+
112
+ attach_function :PyLong_AsLong, [:pointer], :long
113
+ attach_function :PyLong_FromLong, [:pointer], :long
114
+
115
+ #Float Methods
116
+ attach_function :PyFloat_AsDouble, [:pointer], :double
117
+ attach_function :PyFloat_FromDouble, [:double], :pointer
118
+
119
+ #Tuple Methods
120
+ attach_function :PySequence_List, [:pointer], :pointer
121
+ attach_function :PySequence_Tuple, [:pointer], :pointer
122
+ attach_function :PyTuple_Pack, [:int, :varargs], :pointer
123
+
124
+ #Dict/Hash Methods
125
+ attach_function :PyDict_Next, [:pointer, :pointer, :pointer, :pointer], :int
126
+ attach_function :PyDict_New, [], :pointer
127
+ attach_function :PyDict_SetItem, [:pointer, :pointer, :pointer], :int
128
+ attach_function :PyDict_Contains, [:pointer, :pointer], :int
129
+ attach_function :PyDict_GetItem, [:pointer, :pointer], :pointer
130
+
131
+ #Function Constants
132
+ METH_VARARGS = 1
133
+ attach_function :PyCFunction_New, [:pointer, :pointer], :pointer
134
+ callback :PyCFunction, [:pointer, :pointer], :pointer
135
+
136
+ #Error Methods
137
+ attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void
138
+ attach_function :PyErr_Occurred, [], :pointer
139
+ attach_function :PyErr_Clear, [], :void
140
+
141
+ #Reference Counting
142
+ attach_function :Py_IncRef, [:pointer], :void
143
+ attach_function :Py_DecRef, [:pointer], :void
144
+
145
+ #Type Objects
146
+ attach_variable :PyString_Type, DummyStruct.by_value
147
+ attach_variable :PyList_Type, DummyStruct.by_value
148
+ attach_variable :PyInt_Type, DummyStruct.by_value
149
+ attach_variable :PyLong_Type, DummyStruct.by_value
150
+ attach_variable :PyFloat_Type, DummyStruct.by_value
151
+ attach_variable :PyTuple_Type, DummyStruct.by_value
152
+ attach_variable :PyDict_Type, DummyStruct.by_value
153
+ attach_variable :PyFunction_Type, DummyStruct.by_value
154
+ attach_variable :PyCFunction_Type, DummyStruct.by_value
155
+ attach_variable :PyMethod_Type, DummyStruct.by_value
156
+ attach_variable :PyType_Type, DummyStruct.by_value
157
+ attach_variable :PyClass_Type, DummyStruct.by_value
158
+
159
+ attach_variable :Py_TrueStruct, :_Py_TrueStruct, DummyStruct.by_value
160
+ attach_variable :Py_ZeroStruct, :_Py_ZeroStruct, DummyStruct.by_value
161
+ attach_variable :Py_NoneStruct, :_Py_NoneStruct, DummyStruct.by_value
162
+
163
+ #This is an implementation of the basic structure of a Python PyObject
164
+ #struct. The C struct is actually much larger, but since we only access
165
+ #the first two data members via FFI and always deal with struct pointers
166
+ #there is no need to mess around with the rest of the object.
167
+ class PyObjectStruct < FFI::Struct
168
+ layout :ob_refcnt, :int,
169
+ :ob_type, :pointer
170
+ end
171
+
172
+ #This struct is used when defining Python methods.
173
+ class PyMethodDef < FFI::Struct
174
+ layout :ml_name, :pointer,
175
+ :ml_meth, :PyCFunction,
176
+ :ml_flags, :int,
177
+ :ml_doc, :pointer
178
+ end
73
179
 
74
- #Python interpreter startup and shutdown
75
- attach_function :Py_IsInitialized, [], :int
76
- attach_function :Py_Initialize, [], :void
77
- attach_function :Py_Finalize, [], :void
78
-
79
- #Module methods
80
- attach_function :PyImport_ImportModule, [:string], :pointer
81
-
82
- #Object Methods
83
- attach_function :PyObject_HasAttrString, [:pointer, :string], :int
84
- attach_function :PyObject_GetAttrString, [:pointer, :string], :pointer
85
- attach_function :PyObject_SetAttrString, [:pointer, :string, :pointer], :int
86
-
87
- attach_function :PyObject_Compare, [:pointer, :pointer], :int
88
-
89
- attach_function :PyObject_CallObject, [:pointer, :pointer], :pointer
90
- attach_function :PyCallable_Check, [:pointer], :int
91
-
92
- ###Python To Ruby Conversion
93
- #String Methods
94
- attach_function :PyString_AsString, [:pointer], :string
95
- attach_function :PyString_FromString, [:string], :pointer
96
-
97
- #List Methods
98
- attach_function :PyList_GetItem, [:pointer, :int], :pointer
99
- attach_function :PyList_Size, [:pointer], :int
100
- attach_function :PyList_New, [:int], :pointer
101
- attach_function :PyList_SetItem, [:pointer, :int, :pointer], :void
102
-
103
- #Integer Methods
104
- attach_function :PyInt_AsLong, [:pointer], :long
105
- attach_function :PyInt_FromLong, [:long], :pointer
106
-
107
- attach_function :PyLong_AsLong, [:pointer], :long
108
- attach_function :PyLong_FromLong, [:pointer], :long
109
-
110
- #Float Methods
111
- attach_function :PyFloat_AsDouble, [:pointer], :double
112
- attach_function :PyFloat_FromDouble, [:double], :pointer
113
-
114
- #Tuple Methods
115
- attach_function :PySequence_List, [:pointer], :pointer
116
- attach_function :PySequence_Tuple, [:pointer], :pointer
117
- attach_function :PyTuple_Pack, [:int, :varargs], :pointer
118
-
119
- #Dict/Hash Methods
120
- attach_function :PyDict_Next, [:pointer, :pointer, :pointer, :pointer], :int
121
- attach_function :PyDict_New, [], :pointer
122
- attach_function :PyDict_SetItem, [:pointer, :pointer, :pointer], :int
123
- attach_function :PyDict_Contains, [:pointer, :pointer], :int
124
- attach_function :PyDict_GetItem, [:pointer, :pointer], :pointer
125
-
126
- #Error Methods
127
- attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void
128
- attach_function :PyErr_Occurred, [], :pointer
129
- attach_function :PyErr_Clear, [], :void
130
-
131
- #Reference Counting
132
- attach_function :Py_IncRef, [:pointer], :void
133
- attach_function :Py_DecRef, [:pointer], :void
134
-
135
- #Type Objects
136
- attach_variable :PyString_Type, DummyStruct.by_value
137
- attach_variable :PyList_Type, DummyStruct.by_value
138
- attach_variable :PyInt_Type, DummyStruct.by_value
139
- attach_variable :PyLong_Type, DummyStruct.by_value
140
- attach_variable :PyFloat_Type, DummyStruct.by_value
141
- attach_variable :PyTuple_Type, DummyStruct.by_value
142
- attach_variable :PyDict_Type, DummyStruct.by_value
143
- attach_variable :PyFunction_Type, DummyStruct.by_value
144
- attach_variable :PyMethod_Type, DummyStruct.by_value
145
- attach_variable :PyType_Type, DummyStruct.by_value
146
- attach_variable :PyClass_Type, DummyStruct.by_value
147
-
148
- attach_variable :Py_TrueStruct, :_Py_TrueStruct, DummyStruct.by_value
149
- attach_variable :Py_ZeroStruct, :_Py_ZeroStruct, DummyStruct.by_value
150
- attach_variable :Py_NoneStruct, :_Py_NoneStruct, DummyStruct.by_value
151
-
152
- #This is an implementation of the basic structure of a Python PyObject
153
- #struct. The C struct is actually much larger, but since we only access
154
- #the first two data members via FFI and always deal with struct pointers
155
- #there is no need to mess around with the rest of the object.
156
- class PyObjectStruct < FFI::Struct
157
- layout :ob_refcnt, :int,
158
- :ob_type, :pointer
159
180
  end
160
181
 
182
+ else
183
+ module Python; end
161
184
  end
162
185
  end
@@ -13,6 +13,56 @@ module RubyPython
13
13
  #
14
14
  #Note: All RubyPyProxy objects become invalid when the Python interpreter
15
15
  #is halted.
16
+ #
17
+ #Calling Methods With Blocks
18
+ #-----------------------------
19
+ #Any method which is forwarded to a Python object may be called with
20
+ #a block. The result of the method will passed as the argument to
21
+ #that block.
22
+ #
23
+ #@example Supplying a block to a method call
24
+ # irb(main):001:0> RubyPython.start
25
+ # => true
26
+ # irb(main):002:0> RubyPython::PyMain.float(10) do |f|
27
+ # irb(main):003:1* 2*f.rubify
28
+ # irb(main):004:1> end
29
+ # => 20.0
30
+ # irb(main):005:0> RubyPython.stop
31
+ # => true
32
+ #
33
+ #
34
+ #Passing Procs and Methods to methods
35
+ #-------------------------------------
36
+ #RubyPython now supports passing Proc and Method objects to Python
37
+ #methods. The Proc or Method object must be passed explicitly. As
38
+ #seen above, supplying a block to a method will result in the return
39
+ #value of the method call being passed to the block.
40
+ #
41
+ #When a Proc or Method is supplied as a callback, then arguments that
42
+ #it will be called with will be wrapped Python objects.
43
+ #
44
+ #@example Passing a Proc to Python
45
+ # #Python Code
46
+ # def apply_callback(callback, argument):
47
+ # return callback(argument)
48
+ #
49
+ # #IRB Session
50
+ # irb(main):001:0> RubyPython.start
51
+ # => true
52
+ # irb(main):002:0> sys = RubyPython.import 'sys'
53
+ # => <module 'sys' (built-in)>
54
+ # irb(main):003:0> sys.path.append('.')
55
+ # => None
56
+ # irb(main):004:0> sample = RubyPython.import 'sample'
57
+ # => <module 'sample' from './sample.pyc'>
58
+ # irb(main):005:0> callback = Proc.new do |arg|
59
+ # irb(main):006:1* arg * 2
60
+ # irb(main):007:1> end
61
+ # => #<Proc:0x000001018df490@(irb):5>
62
+ # irb(main):008:0> sample.apply_callback(callback, 21).rubify
63
+ # => 42
64
+ # irb(main):009:0> RubyPython.stop
65
+ #
16
66
  class RubyPyProxy < BlankObject
17
67
  include Operators
18
68
 
@@ -119,27 +169,23 @@ module RubyPython
119
169
  end
120
170
 
121
171
  #Returns the string representation of the wrapped object via a call to the
122
- #object's \_\_repr\_\_ method. Falls back on the default Ruby behavior when
123
- #this method cannot be found.
172
+ #object's \_\_repr\_\_ method.
124
173
  #
125
174
  #@return [String]
126
175
  def inspect
127
- self.__repr__.rubify rescue _inspect
128
- rescue
129
- class << self; define_method :_inspect, RubyPyProxy.find_hidden_method(:inspect); end
130
- _inspect
176
+ self.__repr__.rubify
177
+ rescue PythonError, NoMethodError
178
+ RubyPython::PyMain.repr(self).rubify
131
179
  end
132
180
 
133
181
  #Returns the string representation of the wrapped object via a call to the
134
- #object's \_\_str\_\_ method. Falls back on the default Ruby behavior when
135
- #this method cannot be found.
182
+ #object's \_\_str\_\_ method.
136
183
  #
137
184
  #@return [String]
138
185
  def to_s
139
- self.__str__.rubify rescue _to_s
140
- rescue
141
- class << self; define_method :_to_s, RubyPyProxy.find_hidden_method(:to_s); end
142
- _to_s
186
+ self.__str__.rubify
187
+ rescue PythonError, NoMethodError
188
+ RubyPython::PyMain.str(self).rubify
143
189
  end
144
190
 
145
191
  #Converts the wrapped Python object to a Ruby Array. Note that this only converts
@@ -147,7 +193,7 @@ module RubyPython
147
193
  #objects which have an \_\_iter\_\_ method may be converted using to_a.
148
194
  #
149
195
  #Note that for Dict objects, this method returns what you would get in
150
- #Python, not in Ruby i.e. a_dict.to_a returns an array of the
196
+ #Python, not in Ruby i.e. a\_dict.to\_a returns an array of the
151
197
  #dictionary's keys.
152
198
  #@return [Array<RubyPyProxy>]
153
199
  #@example List
@@ -173,7 +219,7 @@ module RubyPython
173
219
  # => [1, 'three']
174
220
  # irb(main):005:0> RubyPython.stop
175
221
  # => true
176
-
222
+ #
177
223
  def to_a
178
224
  iter = self.__iter__
179
225
  ary = []
@@ -3,7 +3,7 @@ module RubyPython
3
3
  module VERSION
4
4
  MAJOR = 0
5
5
  MINOR = 3
6
- TINY = 1
6
+ TINY = 2
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
9
9
  end
data/lib/rubypython.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubypython/core_ext/string'
2
+ require 'rubypython/options'
2
3
  require 'rubypython/python'
3
4
  require 'rubypython/pythonerror'
4
5
  require 'rubypython/pyobject'
@@ -35,6 +36,7 @@ require 'rubypython/pymainclass'
35
36
  #{RubyPython.legacy_mode}.
36
37
  module RubyPython
37
38
 
39
+
38
40
  class << self
39
41
 
40
42
  #Determines whether RubyPython is operating in Normal Mode or Legacy Mode.
@@ -64,14 +66,24 @@ module RubyPython
64
66
 
65
67
  #Starts ups the Python interpreter. This method **must** be run
66
68
  #before using any Python code. The only alternatives are use of the
67
- #{session} and {run} methods.
69
+ #{session} and {run} methods. If the Python interpreter needs to
70
+ #be loaded or reloaded, it will be done here.
68
71
  #@return [Boolean] returns true if the interpreter was started here
69
72
  # and false otherwise
70
73
  def start
74
+ unless @loaded
75
+ @loaded = true
76
+ reload_library
77
+ end
71
78
  if Python.Py_IsInitialized != 0
72
79
  return false
73
80
  end
81
+ if @reload
82
+ reload_library
83
+ @reload = false
84
+ end
74
85
  Python.Py_Initialize
86
+ notify :start
75
87
  true
76
88
  end
77
89
 
@@ -82,11 +94,8 @@ module RubyPython
82
94
  # and false otherwise
83
95
  def stop
84
96
  if Python.Py_IsInitialized !=0
85
- PyMain.main = nil
86
- PyMain.builtin = nil
87
- RubyPython::Operators.send :class_variable_set, '@@operator', nil
88
97
  Python.Py_Finalize
89
- RubyPython::PyObject::AutoPyPointer.current_pointers.clear
98
+ notify :stop
90
99
  return true
91
100
  end
92
101
  false
@@ -121,10 +130,47 @@ module RubyPython
121
130
 
122
131
  #The same as {session} except that the block is executed within the scope
123
132
  #of the RubyPython module.
133
+ #@return the result of evaluating the given block.
124
134
  def run(&block)
125
135
  start
126
136
  result = module_eval(&block)
127
137
  stop
138
+ result
139
+ end
140
+
141
+ private
142
+
143
+ def add_observer(object)
144
+ @observers ||= []
145
+ @observers << object
146
+ true
147
+ end
148
+
149
+ def notify(status)
150
+ if not @observers.nil?
151
+ @observers.each do |o|
152
+ o.update status
153
+ end
154
+ end
128
155
  end
156
+
157
+ def reload_library
158
+ remove_const :Python
159
+ load 'rubypython/python.rb'
160
+ true
161
+ end
162
+
129
163
  end
164
+
165
+ [
166
+ PyMain,
167
+ Operators,
168
+ PyObject::AutoPyPointer
169
+ ].each do |observer|
170
+ add_observer observer
171
+ end
172
+
173
+
130
174
  end
175
+
176
+
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'Callbacks' do
4
+ include TestConstants
5
+
6
+ before do
7
+ RubyPython.start
8
+ @sys = RubyPython.import 'sys'
9
+ @sys.path.append './spec/python_helpers'
10
+ @objects = RubyPython.import 'objects'
11
+ end
12
+
13
+ after do
14
+ RubyPython.start
15
+ end
16
+
17
+ [
18
+ [ 'procs', AProc ],
19
+ [ 'methods', AMethod]
20
+ ].each do |rb_type, rb_object|
21
+ it "accepts #{rb_type} as functions" do
22
+ [
23
+ [2, 2],
24
+ ["a", "Word"],
25
+ [ [1, 2], [3, 4] ]
26
+ ].each do |args|
27
+ @objects.apply_callback(rb_object, args).should == rb_object.call(*args)
28
+ end
29
+ end
30
+ end
31
+
32
+ [
33
+ ["an int", AnInt],
34
+ ["a float", AFloat],
35
+ ["a string", AString],
36
+ ["an array", AnArray],
37
+ ["an array", AnArray],
38
+ ["a hash", AConvertedHash],
39
+ ["true", true],
40
+ ["false", false],
41
+ ["nil", nil]
42
+ ].each do |rb_type, rb_value|
43
+ it "is able to return #{rb_type}" do
44
+ callback = Proc.new do
45
+ rb_value
46
+ end
47
+
48
+ @objects.apply_callback(callback, []).should == rb_value
49
+ end
50
+ end
51
+
52
+ it "is able to be stored by python variables" do
53
+ mockObject = @objects.RubyPythonMockObject.new
54
+ mockObject.callback = AProc
55
+ end
56
+
57
+ it "is callable as a python instance variable" do
58
+ mockObject = @objects.RubyPythonMockObject.new
59
+ mockObject.callback = AProc
60
+ mockObject.callback(2, 2).rubify.should == 4
61
+ end
62
+
63
+ end