lokeshh_rubypython 0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|