lokeshh_rubypython 0.7

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