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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +3 -0
  3. data/.gitignore +18 -0
  4. data/.hgignore +20 -0
  5. data/.hgtags +12 -0
  6. data/.rspec +2 -0
  7. data/Contributors.rdoc +11 -0
  8. data/History.rdoc +214 -0
  9. data/License.rdoc +26 -0
  10. data/Manifest.txt +46 -0
  11. data/PostInstall.txt +16 -0
  12. data/README.rdoc +272 -0
  13. data/Rakefile +118 -0
  14. data/autotest/discover.rb +1 -0
  15. data/lib/rubypython/blankobject.rb +23 -0
  16. data/lib/rubypython/conversion.rb +332 -0
  17. data/lib/rubypython/interpreter.rb +262 -0
  18. data/lib/rubypython/macros.rb +56 -0
  19. data/lib/rubypython/operators.rb +120 -0
  20. data/lib/rubypython/pygenerator.rb +61 -0
  21. data/lib/rubypython/pymainclass.rb +80 -0
  22. data/lib/rubypython/pyobject.rb +189 -0
  23. data/lib/rubypython/python.rb +199 -0
  24. data/lib/rubypython/pythonerror.rb +80 -0
  25. data/lib/rubypython/rubypyproxy.rb +328 -0
  26. data/lib/rubypython/tuple.rb +10 -0
  27. data/lib/rubypython/type.rb +20 -0
  28. data/lib/rubypython.rb +229 -0
  29. data/spec/basic_spec.rb +50 -0
  30. data/spec/callback_spec.rb +53 -0
  31. data/spec/conversion_spec.rb +68 -0
  32. data/spec/pymainclass_spec.rb +24 -0
  33. data/spec/pyobject_spec.rb +202 -0
  34. data/spec/python_helpers/basics.py +23 -0
  35. data/spec/python_helpers/errors.py +2 -0
  36. data/spec/python_helpers/objects.py +48 -0
  37. data/spec/pythonerror_spec.rb +52 -0
  38. data/spec/refcnt_spec.rb +98 -0
  39. data/spec/rubypyclass_spec.rb +10 -0
  40. data/spec/rubypyproxy_spec.rb +261 -0
  41. data/spec/rubypython_spec.rb +59 -0
  42. data/spec/spec_helper.rb +71 -0
  43. data/website/index.rhtml +36 -0
  44. data/website/robots.txt +5 -0
  45. data/website/stylesheets/960.css +549 -0
  46. data/website/stylesheets/border-radius.htc +143 -0
  47. data/website/stylesheets/screen.css +132 -0
  48. 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,10 @@
1
+ module RubyPython
2
+ # A subclass of ::Array that will convert to a Python Tuple automatically.
3
+ class Tuple < ::Array
4
+ def self.tuple(array)
5
+ value = self.new
6
+ value.replace(array.dup)
7
+ value
8
+ end
9
+ end
10
+ 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
@@ -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