rubypython 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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