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.
- data/.rspec +2 -0
- data/History.markdown +5 -0
- data/License.txt +2 -2
- data/Manifest.txt +13 -8
- data/README.markdown +5 -3
- data/Rakefile +7 -5
- data/lib/rubypython/conversion.rb +18 -1
- data/lib/rubypython/legacy.rb +25 -0
- data/lib/rubypython/operators.rb +7 -0
- data/lib/rubypython/options.rb +69 -0
- data/lib/rubypython/pymainclass.rb +13 -2
- data/lib/rubypython/pyobject.rb +17 -5
- data/lib/rubypython/python.rb +176 -153
- data/lib/rubypython/rubypyproxy.rb +60 -14
- data/lib/rubypython/version.rb +1 -1
- data/lib/rubypython.rb +51 -5
- data/spec/callback_spec.rb +63 -0
- data/spec/conversion_spec.rb +17 -9
- data/spec/legacy_spec.rb +41 -2
- data/spec/pymainclass_spec.rb +12 -5
- data/spec/pyobject_spec.rb +44 -37
- data/spec/python_helpers/objects.py +3 -0
- data/spec/python_helpers/objects.pyc +0 -0
- data/spec/pythonerror_spec.rb +13 -6
- data/spec/refcnt_spec.rb +4 -4
- data/spec/rubypyclass_spec.rb +9 -2
- data/spec/rubypyproxy_spec.rb +55 -34
- data/spec/rubypython_spec.rb +41 -8
- data/spec/spec_helper.rb +11 -13
- data.tar.gz.sig +0 -0
- metadata +30 -12
- metadata.gz.sig +0 -0
- data/spec/spec.opts +0 -2
data/lib/rubypython/python.rb
CHANGED
@@ -1,162 +1,185 @@
|
|
1
1
|
require 'ffi'
|
2
2
|
require 'open3'
|
3
|
+
require 'rubypython/options'
|
3
4
|
|
4
5
|
module RubyPython
|
5
|
-
#
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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.
|
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
|
128
|
-
rescue
|
129
|
-
|
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.
|
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
|
140
|
-
rescue
|
141
|
-
|
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.
|
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 = []
|
data/lib/rubypython/version.rb
CHANGED
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
|
-
|
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
|