lokeshh_rubypython 0.7
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.
- checksums.yaml +7 -0
- data/.autotest +3 -0
- data/.gitignore +18 -0
- data/.hgignore +20 -0
- data/.hgtags +12 -0
- data/.rspec +2 -0
- data/Contributors.rdoc +11 -0
- data/History.rdoc +214 -0
- data/License.rdoc +26 -0
- data/Manifest.txt +46 -0
- data/PostInstall.txt +16 -0
- data/README.rdoc +272 -0
- data/Rakefile +118 -0
- data/autotest/discover.rb +1 -0
- data/lib/rubypython/blankobject.rb +23 -0
- data/lib/rubypython/conversion.rb +332 -0
- data/lib/rubypython/interpreter.rb +262 -0
- data/lib/rubypython/macros.rb +56 -0
- data/lib/rubypython/operators.rb +120 -0
- data/lib/rubypython/pygenerator.rb +61 -0
- data/lib/rubypython/pymainclass.rb +80 -0
- data/lib/rubypython/pyobject.rb +189 -0
- data/lib/rubypython/python.rb +199 -0
- data/lib/rubypython/pythonerror.rb +80 -0
- data/lib/rubypython/rubypyproxy.rb +328 -0
- data/lib/rubypython/tuple.rb +10 -0
- data/lib/rubypython/type.rb +20 -0
- data/lib/rubypython.rb +229 -0
- data/spec/basic_spec.rb +50 -0
- data/spec/callback_spec.rb +53 -0
- data/spec/conversion_spec.rb +68 -0
- data/spec/pymainclass_spec.rb +24 -0
- data/spec/pyobject_spec.rb +202 -0
- data/spec/python_helpers/basics.py +23 -0
- data/spec/python_helpers/errors.py +2 -0
- data/spec/python_helpers/objects.py +48 -0
- data/spec/pythonerror_spec.rb +52 -0
- data/spec/refcnt_spec.rb +98 -0
- data/spec/rubypyclass_spec.rb +10 -0
- data/spec/rubypyproxy_spec.rb +261 -0
- data/spec/rubypython_spec.rb +59 -0
- data/spec/spec_helper.rb +71 -0
- data/website/index.rhtml +36 -0
- data/website/robots.txt +5 -0
- data/website/stylesheets/960.css +549 -0
- data/website/stylesheets/border-radius.htc +143 -0
- data/website/stylesheets/screen.css +132 -0
- metadata +297 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
require 'rubypython/pythonerror'
|
|
2
|
+
require 'rubypython/pyobject'
|
|
3
|
+
require 'rubypython/conversion'
|
|
4
|
+
require 'rubypython/operators'
|
|
5
|
+
require 'rubypython/blankobject'
|
|
6
|
+
|
|
7
|
+
module RubyPython
|
|
8
|
+
# In most cases, users will interact with RubyPyProxy objects that hold
|
|
9
|
+
# references to active objects in the \Python interpreter. RubyPyProxy
|
|
10
|
+
# delegates method calls to \Python objects, wrapping and returning the
|
|
11
|
+
# results as RubyPyProxy objects.
|
|
12
|
+
#
|
|
13
|
+
# The allocation, deallocation, and reference counting on RubyPyProxy
|
|
14
|
+
# objects is automatic: RubyPython takes care of it all. When the object
|
|
15
|
+
# is garbage collected, the instance will automatically decrement its
|
|
16
|
+
# object reference count.
|
|
17
|
+
#
|
|
18
|
+
# [NOTE:] All RubyPyProxy objects become invalid when the \Python
|
|
19
|
+
# interpreter is halted.
|
|
20
|
+
#
|
|
21
|
+
# == Calling Methods With Blocks
|
|
22
|
+
# Any method which is forwarded to a \Python object may be called with a
|
|
23
|
+
# block. The result of the method will passed as the argument to that
|
|
24
|
+
# block.
|
|
25
|
+
#
|
|
26
|
+
# RubyPython.run do
|
|
27
|
+
# sys = RubyPython.import 'sys'
|
|
28
|
+
# sys.version { |v| v.rubify.split(' ') }
|
|
29
|
+
# end
|
|
30
|
+
# # => [ "2.6.1", … ]
|
|
31
|
+
#
|
|
32
|
+
# == Passing Procs and Methods to \Python Methods
|
|
33
|
+
# RubyPython supports passing Proc and Method objects to \Python methods.
|
|
34
|
+
# The Proc or Method object must be passed explicitly. As seen above,
|
|
35
|
+
# supplying a block to a method will result in the return value of the
|
|
36
|
+
# method call being passed to the block.
|
|
37
|
+
#
|
|
38
|
+
# When a Proc or Method is supplied as a callback, then arguments that it
|
|
39
|
+
# will be called with will be wrapped \Python objects. It will therefore
|
|
40
|
+
# typically be necessary to write a wrapper around any Ruby callback that
|
|
41
|
+
# requires native Ruby objects.
|
|
42
|
+
#
|
|
43
|
+
# # Python Code: sample.py
|
|
44
|
+
# def apply_callback(callback, argument):
|
|
45
|
+
# return callback(argument)
|
|
46
|
+
#
|
|
47
|
+
# # IRB Session
|
|
48
|
+
# >> RubyPython.start
|
|
49
|
+
# => true
|
|
50
|
+
# >> sys = RubyPython.import 'sys'
|
|
51
|
+
# => <module 'sys' (built-in)>
|
|
52
|
+
# >> sys.path.append('.')
|
|
53
|
+
# => None
|
|
54
|
+
# >> sample = RubyPython.import 'sample'
|
|
55
|
+
# => <module 'sample' from './sample.pyc'>
|
|
56
|
+
# >> callback = Proc.new { |arg| arg * 2 }
|
|
57
|
+
# => # <Proc:0x000001018df490@(irb):5>
|
|
58
|
+
# >> sample.apply_callback(callback, 21).rubify
|
|
59
|
+
# => 42
|
|
60
|
+
# >> RubyPython.stop
|
|
61
|
+
# => true
|
|
62
|
+
class RubyPyProxy < BlankObject
|
|
63
|
+
include Operators
|
|
64
|
+
|
|
65
|
+
attr_reader :pObject
|
|
66
|
+
|
|
67
|
+
# Creates a \Python proxy for the provided Ruby object.
|
|
68
|
+
#
|
|
69
|
+
# Only the following Ruby types can be represented in \Python:
|
|
70
|
+
# * String
|
|
71
|
+
# * Array
|
|
72
|
+
# * Hash
|
|
73
|
+
# * Fixnum
|
|
74
|
+
# * Bignum
|
|
75
|
+
# * Float
|
|
76
|
+
# * Symbol (as a String)
|
|
77
|
+
# * Proc
|
|
78
|
+
# * Method
|
|
79
|
+
# * +true+ (as True)
|
|
80
|
+
# * +false+ (as False)
|
|
81
|
+
# * +nil+ (as None)
|
|
82
|
+
def initialize(pObject)
|
|
83
|
+
if pObject.kind_of? PyObject
|
|
84
|
+
@pObject = pObject
|
|
85
|
+
else
|
|
86
|
+
@pObject = PyObject.new pObject
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Handles the job of wrapping up anything returned by a RubyPyProxy
|
|
91
|
+
# instance. Every returned # object is wrapped by an instance of +RubyPyProxy+
|
|
92
|
+
def _wrap(pyobject)
|
|
93
|
+
if pyobject.class?
|
|
94
|
+
RubyPyClass.new(pyobject)
|
|
95
|
+
else
|
|
96
|
+
RubyPyProxy.new(pyobject)
|
|
97
|
+
end
|
|
98
|
+
rescue Conversion::UnsupportedConversion => exc
|
|
99
|
+
RubyPyProxy.new pyobject
|
|
100
|
+
end
|
|
101
|
+
private :_wrap
|
|
102
|
+
|
|
103
|
+
reveal(:respond_to?)
|
|
104
|
+
|
|
105
|
+
# The standard Ruby +#respond_to?+ method has been renamed to allow
|
|
106
|
+
# RubyPython to query if the proxied \Python object supports the method
|
|
107
|
+
# desired. Setter methods (e.g., +foo=+) are always supported.
|
|
108
|
+
alias :is_real_method? :respond_to?
|
|
109
|
+
|
|
110
|
+
# RubyPython checks the attribute dictionary of the wrapped object to
|
|
111
|
+
# check whether it will respond to a method call. This should not return
|
|
112
|
+
# false positives but it may return false negatives. The built-in Ruby
|
|
113
|
+
# respond_to? method has been aliased to is_real_method?.
|
|
114
|
+
def respond_to?(mname)
|
|
115
|
+
return true if is_real_method?(mname)
|
|
116
|
+
mname = mname.to_s
|
|
117
|
+
return true if mname =~ /=$/
|
|
118
|
+
@pObject.hasAttr(mname)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Delegates method calls to proxied \Python objects.
|
|
122
|
+
#
|
|
123
|
+
# == Delegation Rules
|
|
124
|
+
# 1. If the method ends with a question-mark (e.g., +nil?+), it can only
|
|
125
|
+
# be a Ruby method on RubyPyProxy. Attempt to reveal it (RubyPyProxy
|
|
126
|
+
# is a BlankObject) and call it.
|
|
127
|
+
# 2. If the method ends with equals signs (e.g., +value=+) it's a setter
|
|
128
|
+
# and we can always set an attribute on a \Python object.
|
|
129
|
+
# 3. If the method ends with an exclamation point (e.g., +foo!+) we are
|
|
130
|
+
# attempting to call a method with keyword arguments.
|
|
131
|
+
# 4. The Python method or value will be called, if it's callable.
|
|
132
|
+
# 5. RubyPython will wrap the return value in a RubyPyProxy object
|
|
133
|
+
# 6. If a block has been provided, the wrapped return value will be
|
|
134
|
+
# passed into the block.
|
|
135
|
+
def method_missing(name, *args, &block)
|
|
136
|
+
name = name.to_s
|
|
137
|
+
|
|
138
|
+
if name =~ /\?$/
|
|
139
|
+
begin
|
|
140
|
+
RubyPyProxy.reveal(name.to_sym)
|
|
141
|
+
return self.__send__(name.to_sym, *args, &block)
|
|
142
|
+
rescue RuntimeError => exc
|
|
143
|
+
raise NoMethodError.new(name) if exc.message =~ /Don't know how to reveal/
|
|
144
|
+
raise
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
kwargs = false
|
|
149
|
+
|
|
150
|
+
if name =~ /=$/
|
|
151
|
+
return @pObject.setAttr(name.chomp('='),
|
|
152
|
+
PyObject.new(args.pop))
|
|
153
|
+
elsif name =~ /!$/
|
|
154
|
+
kwargs = true
|
|
155
|
+
name.chomp! "!"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
raise NoMethodError.new(name) if !@pObject.hasAttr(name)
|
|
159
|
+
|
|
160
|
+
pFunc = @pObject.getAttr(name)
|
|
161
|
+
|
|
162
|
+
if pFunc.callable?
|
|
163
|
+
if args.empty? and pFunc.class?
|
|
164
|
+
pReturn = pFunc
|
|
165
|
+
else
|
|
166
|
+
if kwargs and args.last.is_a?(Hash)
|
|
167
|
+
pKeywords = PyObject.new args.pop
|
|
168
|
+
end
|
|
169
|
+
pReturn = _method_call(pFunc, args, pKeywords)
|
|
170
|
+
pFunc.xDecref
|
|
171
|
+
end
|
|
172
|
+
else
|
|
173
|
+
pReturn = pFunc
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
result = _wrap(pReturn)
|
|
177
|
+
|
|
178
|
+
if block
|
|
179
|
+
block.call(result)
|
|
180
|
+
else
|
|
181
|
+
result
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
#Handles the of calling a wrapped callable Python object at a higher level
|
|
186
|
+
#than +PyObject#callObject+. For internal use only.
|
|
187
|
+
def _method_call(pFunc, args, pKeywords)
|
|
188
|
+
pTuple = PyObject.buildArgTuple(*args)
|
|
189
|
+
pReturn = if pKeywords
|
|
190
|
+
pFunc.callObjectKeywords(pTuple, pKeywords)
|
|
191
|
+
else
|
|
192
|
+
pFunc.callObject(pTuple)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Clean up unused Python vars instead of waiting on Ruby's GC to
|
|
196
|
+
# do it.
|
|
197
|
+
pTuple.xDecref
|
|
198
|
+
pKeywords.xDecref if pKeywords
|
|
199
|
+
raise PythonError.handle_error if PythonError.error?
|
|
200
|
+
pReturn
|
|
201
|
+
end
|
|
202
|
+
private :_method_call
|
|
203
|
+
|
|
204
|
+
# RubyPython will attempt to translate the wrapped object into a native
|
|
205
|
+
# Ruby object. This will only succeed for simple built-in type.
|
|
206
|
+
def rubify
|
|
207
|
+
converted = @pObject.rubify
|
|
208
|
+
if converted.kind_of? ::FFI::Pointer
|
|
209
|
+
converted = self.class.new converted
|
|
210
|
+
end
|
|
211
|
+
converted
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Returns the String representation of the wrapped object via a call to
|
|
215
|
+
# the object's <tt>__repr__</tt> method, or the +repr+ method in PyMain.
|
|
216
|
+
def inspect
|
|
217
|
+
self.__repr__.rubify
|
|
218
|
+
rescue PythonError, NoMethodError
|
|
219
|
+
RubyPython::PyMain.repr(self).rubify
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Returns the string representation of the wrapped object via a call to
|
|
223
|
+
# the object's <tt>__str__</tt> method or the +str+ method in PyMain.
|
|
224
|
+
def to_s
|
|
225
|
+
self.__str__.rubify
|
|
226
|
+
rescue PythonError, NoMethodError
|
|
227
|
+
RubyPython::PyMain.str(self).rubify
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Converts the wrapped \Python object to a Ruby Array. Note that this
|
|
231
|
+
# only converts one level, so a nested array will remain a proxy object.
|
|
232
|
+
# Only wrapped objects which have an <tt>__iter__</tt> method may be
|
|
233
|
+
# converted using +to_a+.
|
|
234
|
+
#
|
|
235
|
+
# Note that for \Python Dict objects, this method returns what you would
|
|
236
|
+
# get in \Python, not in Ruby: +a_dict.to_a+ returns an array of the
|
|
237
|
+
# dictionary's keys.
|
|
238
|
+
#
|
|
239
|
+
# === List #to_a Returns an Array
|
|
240
|
+
# >> RubyPython.start
|
|
241
|
+
# => true
|
|
242
|
+
# >> list = RubyPython::RubyPyProxy.new([1, 'a', 2, 'b'])
|
|
243
|
+
# => [1, 'a', 2, 'b']
|
|
244
|
+
# >> list.kind_of? RubyPython::RubyPyProxy
|
|
245
|
+
# => true
|
|
246
|
+
# >> list.to_a
|
|
247
|
+
# => [1, 'a', 2, 'b']
|
|
248
|
+
# >> RubyPython.stop
|
|
249
|
+
# => true
|
|
250
|
+
#
|
|
251
|
+
# === Dict #to_a Returns An Array of Keys
|
|
252
|
+
# >> RubyPython.start
|
|
253
|
+
# => true
|
|
254
|
+
# >> dict = RubyPython::RubyPyProxy.new({1 => '2', :three => [4,5]})
|
|
255
|
+
# => {1: '2', 'three': [4, 5]}
|
|
256
|
+
# >> dict.kind_of? RubyPython::RubyPyProxy
|
|
257
|
+
# => true
|
|
258
|
+
# >> dict.to_a
|
|
259
|
+
# => [1, 'three']
|
|
260
|
+
# >> RubyPython.stop
|
|
261
|
+
# => true
|
|
262
|
+
#
|
|
263
|
+
# === Non-Array Values Do Not Convert
|
|
264
|
+
# >> RubyPython.start
|
|
265
|
+
# => true
|
|
266
|
+
# >> item = RubyPython::RubyPyProxy.new(42)
|
|
267
|
+
# => 42
|
|
268
|
+
# >> item.to_a
|
|
269
|
+
# NoMethodError: __iter__
|
|
270
|
+
def to_a
|
|
271
|
+
iter = self.__iter__
|
|
272
|
+
ary = []
|
|
273
|
+
loop do
|
|
274
|
+
ary << iter.next()
|
|
275
|
+
end
|
|
276
|
+
rescue PythonError => exc
|
|
277
|
+
raise if exc.message !~ /StopIteration/
|
|
278
|
+
ary
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Returns the methods on the \Python object by calling the +dir+
|
|
282
|
+
# built-in.
|
|
283
|
+
def methods
|
|
284
|
+
pObject.dir.map { |x| x.to_sym }
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Creates a PyEnumerable for this object. The object must have the
|
|
288
|
+
# <tt>__iter__</tt> method.
|
|
289
|
+
def to_enum
|
|
290
|
+
PyEnumerable.new(@pObject)
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# A class to wrap \Python modules. It behaves exactly the same as
|
|
295
|
+
# RubyPyProxy. It is just here for Bookkeeping and aesthetics.
|
|
296
|
+
class RubyPyModule < RubyPyProxy; end
|
|
297
|
+
|
|
298
|
+
# A class to wrap \Python classes.
|
|
299
|
+
class RubyPyClass < RubyPyProxy
|
|
300
|
+
# Create an instance of the wrapped class. This is a workaround for the
|
|
301
|
+
# fact that \Python classes are meant to be callable.
|
|
302
|
+
def new(*args)
|
|
303
|
+
pReturn = _method_call(@pObject, args, nil)
|
|
304
|
+
RubyPyInstance.new pReturn
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# An object representing an instance of a \Python class. It behaves
|
|
309
|
+
# exactly the same as RubyPyProxy. It is just here for Bookkeeping and
|
|
310
|
+
# aesthetics.
|
|
311
|
+
class RubyPyInstance < RubyPyProxy; end
|
|
312
|
+
|
|
313
|
+
# An object representing a Python enumerable object.
|
|
314
|
+
class PyEnumerable < RubyPyProxy
|
|
315
|
+
include Enumerable
|
|
316
|
+
|
|
317
|
+
def each
|
|
318
|
+
iter = self.__iter__
|
|
319
|
+
loop do
|
|
320
|
+
begin
|
|
321
|
+
yield iter.next
|
|
322
|
+
rescue RubyPython::PythonError => exc
|
|
323
|
+
return if exc.message =~ /StopIteration/
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module RubyPython
|
|
2
|
+
# Creates a Ruby class that inherits from a proxied Python object.
|
|
3
|
+
def self.Type(name)
|
|
4
|
+
mod, match, klass = name.rpartition(".")
|
|
5
|
+
pymod = RubyPython.import(mod)
|
|
6
|
+
pyclass = pymod.pObject.getAttr(klass)
|
|
7
|
+
rclass = Class.new(RubyPyProxy) do
|
|
8
|
+
define_method(:initialize) do |*args|
|
|
9
|
+
args = PyObject.convert(*args)
|
|
10
|
+
pTuple = PyObject.buildArgTuple(*args)
|
|
11
|
+
pReturn = pyclass.callObject(pTuple)
|
|
12
|
+
if PythonError.error?
|
|
13
|
+
raise PythonError.handle_error
|
|
14
|
+
end
|
|
15
|
+
@pObject = pReturn
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
return rclass
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/rubypython.rb
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# RubyPython is a bridge between the Ruby and \Python interpreters. It
|
|
2
|
+
# embeds a \Python interpreter in the Ruby application's process using FFI
|
|
3
|
+
# and provides a means for wrapping, converting, and calling \Python objects
|
|
4
|
+
# and methods.
|
|
5
|
+
#
|
|
6
|
+
# == Usage
|
|
7
|
+
# The \Python interpreter must be started before the RubyPython bridge is
|
|
8
|
+
# functional. The user can either manually manage the running of the
|
|
9
|
+
# interpreter as shown below, or use the +RubyPython.run+ or
|
|
10
|
+
# +RubyPython.session+ methods to automatically start and stop the
|
|
11
|
+
# interpreter.
|
|
12
|
+
#
|
|
13
|
+
# RubyPython.start
|
|
14
|
+
# cPickle = RubyPython.import "cPickle"
|
|
15
|
+
# puts cPickle.dumps("RubyPython is awesome!").rubify
|
|
16
|
+
# RubyPython.stop
|
|
17
|
+
module RubyPython
|
|
18
|
+
VERSION = '0.6.4'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'rubypython/blankobject'
|
|
22
|
+
require 'rubypython/interpreter'
|
|
23
|
+
require 'rubypython/python'
|
|
24
|
+
require 'rubypython/pythonerror'
|
|
25
|
+
require 'rubypython/pyobject'
|
|
26
|
+
require 'rubypython/rubypyproxy'
|
|
27
|
+
require 'rubypython/pymainclass'
|
|
28
|
+
require 'rubypython/pygenerator'
|
|
29
|
+
require 'rubypython/tuple'
|
|
30
|
+
require 'thread'
|
|
31
|
+
|
|
32
|
+
module RubyPython
|
|
33
|
+
class << self
|
|
34
|
+
## Starts the \Python interpreter. One of +RubyPython.start+,
|
|
35
|
+
# RubyPython.session+, or +RubyPython.run+ must be run before using any
|
|
36
|
+
# \Python code. Returns +true+ if the interpreter was started; +false+
|
|
37
|
+
# otherwise.
|
|
38
|
+
#
|
|
39
|
+
# [options] Configures the interpreter prior to starting it. Principally
|
|
40
|
+
# used to provide an alternative \Python interpreter to start.
|
|
41
|
+
#
|
|
42
|
+
# With no options provided:
|
|
43
|
+
# RubyPython.start
|
|
44
|
+
# sys = RubyPython.import 'sys'
|
|
45
|
+
# p sys.version # => "2.6.6"
|
|
46
|
+
# RubyPython.stop
|
|
47
|
+
#
|
|
48
|
+
# With an alternative \Python executable:
|
|
49
|
+
# RubyPython.start(:python_exe => 'python2.7')
|
|
50
|
+
# sys = RubyPython.import 'sys'
|
|
51
|
+
# p sys.version # => "2.7.1"
|
|
52
|
+
# RubyPython.stop
|
|
53
|
+
def start(options = {})
|
|
54
|
+
RubyPython::Python.synchronize do
|
|
55
|
+
# Has the Runtime interpreter been defined?
|
|
56
|
+
if self.const_defined?(:Runtime)
|
|
57
|
+
# If this constant is defined, then yes it is. Since it is, let's
|
|
58
|
+
# see if we should print a warning to the user.
|
|
59
|
+
unless Runtime == options
|
|
60
|
+
warn "The Python interpreter has already been loaded from #{Runtime.python} and cannot be changed in this process. Continuing with the current runtime."
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
interp = RubyPython::Interpreter.new(options)
|
|
64
|
+
if interp.valid?
|
|
65
|
+
self.const_set(:Runtime, interp)
|
|
66
|
+
else
|
|
67
|
+
raise RubyPython::InvalidInterpreter, "An invalid interpreter was specified."
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
unless defined? RubyPython::Python.ffi_libraries
|
|
72
|
+
Runtime.__send__(:infect!, RubyPython::Python)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
return false if RubyPython::Python.Py_IsInitialized != 0
|
|
76
|
+
RubyPython::Python.Py_Initialize
|
|
77
|
+
notify :start
|
|
78
|
+
true
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Stops the \Python interpreter if it is running. Returns +true+ if the
|
|
83
|
+
# intepreter is stopped. All wrapped \Python objects are invalid after
|
|
84
|
+
# invocation of this method. If you need the values within the \Python
|
|
85
|
+
# proxy objects, be sure to call +RubyPyProxy#rubify+ on them.
|
|
86
|
+
def stop
|
|
87
|
+
RubyPython::Python.synchronize do
|
|
88
|
+
if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
|
|
89
|
+
Python.Py_Finalize
|
|
90
|
+
notify :stop
|
|
91
|
+
true
|
|
92
|
+
else
|
|
93
|
+
false
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Import a \Python module into the interpreter and return a proxy object
|
|
99
|
+
# for it.
|
|
100
|
+
#
|
|
101
|
+
# This is the preferred way to gain access to \Python objects.
|
|
102
|
+
#
|
|
103
|
+
# [mod_name] The name of the module to import.
|
|
104
|
+
def import(mod_name)
|
|
105
|
+
if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
|
|
106
|
+
pModule = Python.PyImport_ImportModule mod_name
|
|
107
|
+
raise PythonError.handle_error if PythonError.error?
|
|
108
|
+
pymod = PyObject.new pModule
|
|
109
|
+
RubyPyModule.new(pymod)
|
|
110
|
+
else
|
|
111
|
+
raise "Python has not been started."
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Starts the \Python interpreter (optionally with options) and +yields+
|
|
116
|
+
# to the provided block. When the block exits for any reason, the
|
|
117
|
+
# \Python interpreter is stopped automatically.
|
|
118
|
+
#
|
|
119
|
+
# The last executed expression of the block is returned. Be careful that
|
|
120
|
+
# the last expression of the block does not return a RubyPyProxy object,
|
|
121
|
+
# because the proxy object will be invalidated when the interpreter is
|
|
122
|
+
# stopped.
|
|
123
|
+
#
|
|
124
|
+
# [options] Configures the interpreter prior to starting it. Principally
|
|
125
|
+
# used to provide an alternative \Python interpreter to start.
|
|
126
|
+
#
|
|
127
|
+
# *NOTE*: In the current version of RubyPython, it _is_ possible to change
|
|
128
|
+
# \Python interpreters in a single Ruby process execution, but it is
|
|
129
|
+
# *strongly* discouraged as this may lead to segmentation faults. This
|
|
130
|
+
# feature is highly experimental and may be disabled in the future.
|
|
131
|
+
#
|
|
132
|
+
# :call-seq:
|
|
133
|
+
# session(options = {}) { block to execute }
|
|
134
|
+
def session(options = {})
|
|
135
|
+
start(options)
|
|
136
|
+
yield
|
|
137
|
+
ensure
|
|
138
|
+
stop
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Starts the \Python interpreter (optionally with options) and executes
|
|
142
|
+
# the provided block in the RubyPython module scope. When the block
|
|
143
|
+
# exits for any reason, the \Python interpreter is stopped
|
|
144
|
+
# automatically.
|
|
145
|
+
#
|
|
146
|
+
# The last executed expression of the block is returned. Be careful that
|
|
147
|
+
# the last expression of the block does not return a RubyPyProxy object,
|
|
148
|
+
# because the proxy object will be invalidated when the interpreter is
|
|
149
|
+
# stopped.
|
|
150
|
+
#
|
|
151
|
+
# [options] Configures the interpreter prior to starting it. Principally
|
|
152
|
+
# used to provide an alternative \Python interpreter to start.
|
|
153
|
+
#
|
|
154
|
+
# *NOTE*: In the current version of RubyPython, it _is_ possible to
|
|
155
|
+
# change \Python interpreters in a single Ruby process execution, but it
|
|
156
|
+
# is *strongly* discouraged as this may lead to segmentation faults.
|
|
157
|
+
# This feature is highly experimental and may be disabled in the future.
|
|
158
|
+
#
|
|
159
|
+
# :call-seq:
|
|
160
|
+
# run(options = {}) { block to execute in RubyPython context }
|
|
161
|
+
def run(options = {}, &block)
|
|
162
|
+
start(options)
|
|
163
|
+
self.module_eval(&block)
|
|
164
|
+
ensure
|
|
165
|
+
stop
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Starts the \Python interpreter for a
|
|
169
|
+
# {virtualenv}[http://pypi.python.org/pypi/virtualenv] virtual
|
|
170
|
+
# environment. Returns +true+ if the interpreter was started.
|
|
171
|
+
#
|
|
172
|
+
# [virtualenv] The root path to the virtualenv-installed \Python
|
|
173
|
+
# interpreter.
|
|
174
|
+
#
|
|
175
|
+
# RubyPython.start_from_virtualenv('/path/to/virtualenv')
|
|
176
|
+
# sys = RubyPython.import 'sys'
|
|
177
|
+
# p sys.version # => "2.7.1"
|
|
178
|
+
# RubyPython.stop
|
|
179
|
+
#
|
|
180
|
+
# *NOTE*: In the current version of RubyPython, it _is_ possible to
|
|
181
|
+
# change \Python interpreters in a single Ruby process execution, but it
|
|
182
|
+
# is *strongly* discouraged as this may lead to segmentation faults.
|
|
183
|
+
# This feature is highly experimental and may be disabled in the future.
|
|
184
|
+
def start_from_virtualenv(virtualenv)
|
|
185
|
+
result = start(:python_exe => File.join(virtualenv, "bin", "python"))
|
|
186
|
+
activate_virtualenv
|
|
187
|
+
result
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Returns an object describing the active Python interpreter. Returns
|
|
191
|
+
# +nil+ if there is no active interpreter.
|
|
192
|
+
def python
|
|
193
|
+
if self.const_defined? :Runtime
|
|
194
|
+
self::Runtime
|
|
195
|
+
else
|
|
196
|
+
nil
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Used to activate the virtualenv.
|
|
201
|
+
def activate_virtualenv
|
|
202
|
+
imp = import("imp")
|
|
203
|
+
imp.load_source("activate_this",
|
|
204
|
+
File.join(File.dirname(RubyPython::Runtime.python),
|
|
205
|
+
"activate_this.py"))
|
|
206
|
+
end
|
|
207
|
+
private :activate_virtualenv
|
|
208
|
+
|
|
209
|
+
def add_observer(object)
|
|
210
|
+
@observers ||= []
|
|
211
|
+
@observers << object
|
|
212
|
+
true
|
|
213
|
+
end
|
|
214
|
+
private :add_observer
|
|
215
|
+
|
|
216
|
+
def notify(status)
|
|
217
|
+
@observers ||= []
|
|
218
|
+
@observers.each do |o|
|
|
219
|
+
next if nil === o
|
|
220
|
+
o.__send__ :python_interpreter_update, status
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
private :notify
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
add_observer PyMain
|
|
227
|
+
add_observer Operators
|
|
228
|
+
add_observer PyObject::AutoPyPointer
|
|
229
|
+
end
|
data/spec/basic_spec.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include TestConstants
|
|
4
|
+
|
|
5
|
+
describe "RubyPython" do
|
|
6
|
+
it "can import and use a native extension like cPickle" do
|
|
7
|
+
cPickle = RubyPython.import("cPickle")
|
|
8
|
+
string = cPickle.dumps("Testing RubyPython.")
|
|
9
|
+
string.should_not be_a_kind_of String
|
|
10
|
+
string.rubify.should be_a_kind_of String
|
|
11
|
+
string.rubify.should ~ /S'Testing RubyPython.'\n/
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "can import and use a pure Python extension like pickle" do
|
|
15
|
+
pickle = RubyPython.import("pickle")
|
|
16
|
+
string = pickle.dumps("Testing RubyPython.")
|
|
17
|
+
string.should_not be_a_kind_of String
|
|
18
|
+
string.rubify.should be_a_kind_of String
|
|
19
|
+
string.rubify.should ~ /S'Testing RubyPython.'\n/
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "can use iterators from Python" do
|
|
23
|
+
items = []
|
|
24
|
+
@basics.iterate_list.to_enum.each { |item| items << item }
|
|
25
|
+
items.should == [ 1, 2, 3 ]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "can use Ruby procs as callbacks to Python code" do
|
|
29
|
+
@basics.simple_callback(lambda { |v| v * v }, 4).should == 16
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "can use Ruby methods as callbacks to Python code" do
|
|
33
|
+
def triple(v)
|
|
34
|
+
v * 3
|
|
35
|
+
end
|
|
36
|
+
@basics.simple_callback(method(:triple), 4).should == 12
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "can feed a Python generator in Ruby 1.9", :ruby_version => '1.9' do
|
|
40
|
+
output = @basics.simple_generator(RubyPython.generator do
|
|
41
|
+
(1..10).each { |i| RubyPython.yield i }
|
|
42
|
+
end)
|
|
43
|
+
output.should == [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "can use named parameters to functions" do
|
|
47
|
+
@basics.named_args(2, 1).should == [ 2, 1 ]
|
|
48
|
+
@basics.named_args!(:arg2 => 2, :arg1 => 1).should == [ 1, 2 ]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
|
2
|
+
|
|
3
|
+
include TestConstants
|
|
4
|
+
|
|
5
|
+
describe 'Callbacks' do
|
|
6
|
+
{
|
|
7
|
+
'procs' => AProc,
|
|
8
|
+
'methods' => AMethod,
|
|
9
|
+
}.each do |rb_type, rb_object|
|
|
10
|
+
it "accepts #{rb_type} as functions" do
|
|
11
|
+
[
|
|
12
|
+
[2, 2],
|
|
13
|
+
["a", "Word"],
|
|
14
|
+
[ [1, 2], [3, 4] ]
|
|
15
|
+
].each do |args|
|
|
16
|
+
@objects.apply_callback(rb_object, args).should == rb_object.call(*args)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
[
|
|
22
|
+
["an int", AnInt],
|
|
23
|
+
["a float", AFloat],
|
|
24
|
+
["a string", AString],
|
|
25
|
+
["a string with nulls", AStringWithNULLs],
|
|
26
|
+
["an array", AnArray],
|
|
27
|
+
["an array", AnArray],
|
|
28
|
+
["a hash", AConvertedHash],
|
|
29
|
+
["true", true],
|
|
30
|
+
["false", false],
|
|
31
|
+
["nil", nil]
|
|
32
|
+
].each do |rb_type, rb_value|
|
|
33
|
+
it "is able to return #{rb_type}" do
|
|
34
|
+
callback = Proc.new do
|
|
35
|
+
rb_value
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@objects.apply_callback(callback, []).should == rb_value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "is able to be stored by python variables" do
|
|
43
|
+
mockObject = @objects.RubyPythonMockObject.new
|
|
44
|
+
mockObject.callback = AProc
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "is callable as a python instance variable" do
|
|
48
|
+
mockObject = @objects.RubyPythonMockObject.new
|
|
49
|
+
mockObject.callback = AProc
|
|
50
|
+
mockObject.callback(2, 2).rubify.should == 4
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|