rubypython 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|