rubypython 0.3.2 → 0.5.0

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 (51) hide show
  1. data/.autotest +3 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +13 -0
  4. data/.hgignore +14 -0
  5. data/.hgtags +7 -0
  6. data/.rspec +1 -1
  7. data/Contributors.rdoc +9 -0
  8. data/History.rdoc +148 -0
  9. data/{License.txt → License.rdoc} +7 -1
  10. data/Manifest.txt +15 -10
  11. data/PostInstall.txt +11 -4
  12. data/README.rdoc +272 -0
  13. data/Rakefile +107 -22
  14. data/autotest/discover.rb +1 -0
  15. data/lib/rubypython.rb +214 -120
  16. data/lib/rubypython/blankobject.rb +16 -14
  17. data/lib/rubypython/conversion.rb +242 -173
  18. data/lib/rubypython/legacy.rb +30 -31
  19. data/lib/rubypython/macros.rb +43 -34
  20. data/lib/rubypython/operators.rb +103 -101
  21. data/lib/rubypython/options.rb +41 -44
  22. data/lib/rubypython/pygenerator.rb +61 -0
  23. data/lib/rubypython/pymainclass.rb +46 -29
  24. data/lib/rubypython/pyobject.rb +193 -177
  25. data/lib/rubypython/python.rb +189 -176
  26. data/lib/rubypython/pythonerror.rb +54 -63
  27. data/lib/rubypython/pythonexec.rb +123 -0
  28. data/lib/rubypython/rubypyproxy.rb +213 -137
  29. data/lib/rubypython/type.rb +20 -0
  30. data/spec/basic_spec.rb +50 -0
  31. data/spec/callback_spec.rb +7 -17
  32. data/spec/conversion_spec.rb +7 -21
  33. data/spec/legacy_spec.rb +1 -16
  34. data/spec/pymainclass_spec.rb +6 -15
  35. data/spec/pyobject_spec.rb +39 -64
  36. data/spec/python_helpers/basics.py +20 -0
  37. data/spec/python_helpers/objects.py +24 -20
  38. data/spec/pythonerror_spec.rb +5 -17
  39. data/spec/refcnt_spec.rb +4 -10
  40. data/spec/rubypyclass_spec.rb +1 -11
  41. data/spec/rubypyproxy_spec.rb +45 -54
  42. data/spec/rubypython_spec.rb +45 -57
  43. data/spec/spec_helper.rb +49 -33
  44. metadata +87 -63
  45. data.tar.gz.sig +0 -0
  46. data/History.markdown +0 -97
  47. data/README.markdown +0 -105
  48. data/lib/rubypython/core_ext/string.rb +0 -7
  49. data/lib/rubypython/version.rb +0 -9
  50. data/spec/python_helpers/objects.pyc +0 -0
  51. metadata.gz.sig +0 -0
@@ -0,0 +1,123 @@
1
+ # A class that represents a \Python executable.
2
+ #
3
+ # End users may get the instance that represents the current running \Python
4
+ # interpreter (from +RubyPython.python+), but should not directly
5
+ # instantiate this class.
6
+ class RubyPython::PythonExec
7
+ # Based on the name of or path to the \Python executable provided, will
8
+ # determine:
9
+ #
10
+ # * The full path to the \Python executable.
11
+ # * The version of \Python being run.
12
+ # * The system prefix.
13
+ # * The main loadable \Python library for this version.
14
+ def initialize(python_executable)
15
+ @python = python_executable || "python"
16
+ @python = %x(#{@python} -c "import sys; print sys.executable").chomp
17
+
18
+ @version = run_command 'import sys; print "%d.%d" % sys.version_info[:2]'
19
+
20
+ @realname = @python.dup
21
+ if @realname !~ /#{@version}$/
22
+ @realname = "#{@python}#{@version}"
23
+ end
24
+ @basename = File.basename(@realname)
25
+
26
+ @sys_prefix = run_command 'import sys; print sys.prefix'
27
+ @library = find_python_lib
28
+ end
29
+
30
+ def find_python_lib
31
+ # By default, the library name will be something like
32
+ # libpython2.6.so, but that won't always work.
33
+ libbase = "#{FFI::Platform::LIBPREFIX}#{@basename}"
34
+ libext = FFI::Platform::LIBSUFFIX
35
+ libname = "#{libbase}.#{libext}"
36
+
37
+ # We may need to look in multiple locations for Python, so let's
38
+ # build this as an array.
39
+ locations = [ File.join(@sys_prefix, "lib", libname) ]
40
+
41
+ if FFI::Platform.mac?
42
+ # On the Mac, let's add a special case that has even a different
43
+ # libname. This may not be fully useful on future versions of OS
44
+ # X, but it should work on 10.5 and 10.6. Even if it doesn't, the
45
+ # next step will (/usr/lib/libpython<version>.dylib is a symlink
46
+ # to the correct location).
47
+ locations << File.join(@sys_prefix, "Python")
48
+ # Let's also look in the location that was originally set in this
49
+ # library:
50
+ File.join(@sys_prefix, "lib", "#{@realname}", "config", libname)
51
+ end
52
+
53
+ if FFI::Platform.unix?
54
+ # On Unixes, let's look in some standard alternative places, too.
55
+ # Just in case. Some Unixes don't include a .so symlink when they
56
+ # should, so let's look for the base case of .so.1, too.
57
+ [ libname, "#{libname}.1" ].each do |name|
58
+ locations << File.join("/opt/local/lib", name)
59
+ locations << File.join("/opt/lib", name)
60
+ locations << File.join("/usr/local/lib", name)
61
+ locations << File.join("/usr/lib", name)
62
+ end
63
+ end
64
+
65
+ # Let's add alternative extensions; again, just in case.
66
+ locations.dup.each do |location|
67
+ path = File.dirname(location)
68
+ base = File.basename(location, ".#{libext}")
69
+ locations << File.join(path, "#{base}.so") # Standard Unix
70
+ locations << File.join(path, "#{base}.dylib") # Mac OS X
71
+ locations << File.join(path, "#{base}.dll") # Windows
72
+ locations << File.join(path, "#{base}.a") # Non-DLL
73
+ end
74
+
75
+ # Remove redundant locations
76
+ locations.uniq!
77
+
78
+ library = nil
79
+
80
+ locations.each do |location|
81
+ if File.exists? location
82
+ library = location
83
+ break
84
+ end
85
+ end
86
+
87
+ library
88
+ end
89
+ private :find_python_lib
90
+
91
+ # The python executable to use.
92
+ attr_reader :python
93
+ # The real name of the python executable (with version).
94
+ attr_reader :realname
95
+ # The sys.prefix for Python.
96
+ attr_reader :sys_prefix
97
+ # The Python library.
98
+ attr_reader :library
99
+ # The version
100
+ attr_reader :version
101
+
102
+ # Run a Python command-line command.
103
+ def run_command(command)
104
+ %x(#{@python} -c '#{command}').chomp if @python
105
+ end
106
+
107
+ def to_s
108
+ @realname
109
+ end
110
+
111
+ def inspect
112
+ if @python
113
+ "#<#{realname} #{sys_prefix}>"
114
+ else
115
+ "#<invalid interpreter>"
116
+ end
117
+ end
118
+
119
+ def invalidate!
120
+ @python = @version = @realname = @sys_prefix = @library = nil
121
+ end
122
+ private :invalidate!
123
+ end
@@ -5,69 +5,80 @@ require 'rubypython/operators'
5
5
  require 'rubypython/blankobject'
6
6
 
7
7
  module RubyPython
8
- #This is the object that the end user will most often be interacting
9
- #with. It holds a reference to an object in the Python VM an delegates
10
- #method calls to it, wrapping and returning the results. The user should
11
- #not worry about reference counting of this object an instance
12
- #will decrement its objects reference count when it is garbage collected.
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.
13
12
  #
14
- #Note: All RubyPyProxy objects become invalid when the Python interpreter
15
- #is halted.
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.
16
17
  #
17
- #Calling Methods With Blocks
18
- #-----------------------------
19
- #Any method which is forwarded to a Python object may be called with
20
- #a block. The result of the method will passed as the argument to
21
- #that block.
18
+ # [NOTE:] All RubyPyProxy objects become invalid when the \Python
19
+ # interpreter is halted.
22
20
  #
23
- #@example Supplying a block to a method call
24
- # irb(main):001:0> RubyPython.start
25
- # => true
26
- # irb(main):002:0> RubyPython::PyMain.float(10) do |f|
27
- # irb(main):003:1* 2*f.rubify
28
- # irb(main):004:1> end
29
- # => 20.0
30
- # irb(main):005:0> RubyPython.stop
31
- # => true
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.
32
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.
33
37
  #
34
- #Passing Procs and Methods to methods
35
- #-------------------------------------
36
- #RubyPython now supports passing Proc and Method objects to Python
37
- #methods. The Proc or Method object must be passed explicitly. As
38
- #seen above, supplying a block to a method will result in the return
39
- #value of the method call being passed to the block.
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.
40
42
  #
41
- #When a Proc or Method is supplied as a callback, then arguments that
42
- #it will be called with will be wrapped Python objects.
43
- #
44
- #@example Passing a Proc to Python
45
- # #Python Code
46
- # def apply_callback(callback, argument):
47
- # return callback(argument)
48
- #
49
- # #IRB Session
50
- # irb(main):001:0> RubyPython.start
51
- # => true
52
- # irb(main):002:0> sys = RubyPython.import 'sys'
53
- # => <module 'sys' (built-in)>
54
- # irb(main):003:0> sys.path.append('.')
55
- # => None
56
- # irb(main):004:0> sample = RubyPython.import 'sample'
57
- # => <module 'sample' from './sample.pyc'>
58
- # irb(main):005:0> callback = Proc.new do |arg|
59
- # irb(main):006:1* arg * 2
60
- # irb(main):007:1> end
61
- # => #<Proc:0x000001018df490@(irb):5>
62
- # irb(main):008:0> sample.apply_callback(callback, 21).rubify
63
- # => 42
64
- # irb(main):009:0> RubyPython.stop
43
+ # # Python Code: sample.py
44
+ # def apply_callback(callback, argument):
45
+ # return callback(argument)
65
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
66
62
  class RubyPyProxy < BlankObject
67
63
  include Operators
68
64
 
69
65
  attr_reader :pObject
70
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)
71
82
  def initialize(pObject)
72
83
  if pObject.kind_of? PyObject
73
84
  @pObject = pObject
@@ -76,12 +87,12 @@ module RubyPython
76
87
  end
77
88
  end
78
89
 
79
- #Handles the job of wrapping up anything returned by a {RubyPyProxy}
80
- #instance. The behavior differs depending on the value of
81
- #{RubyPython.legacy_mode}. If legacy mode is inactive, every returned
82
- #object is wrapped by an instance of {RubyPyProxy}. If legacy mode is
83
- #active, RubyPython first attempts to convert the returned object to a
84
- #native Ruby type, and then only wraps the object if this fails.
90
+ # Handles the job of wrapping up anything returned by a RubyPyProxy
91
+ # instance. The behavior differs depending on the value of
92
+ # +RubyPython.legacy_mode+. If legacy mode is inactive, every returned
93
+ # object is wrapped by an instance of +RubyPyProxy+. If legacy mode is
94
+ # active, RubyPython first attempts to convert the returned object to a
95
+ # native Ruby type, and then only wraps the object if this fails.
85
96
  def _wrap(pyobject)
86
97
  if pyobject.class?
87
98
  RubyPyClass.new(pyobject)
@@ -93,28 +104,45 @@ module RubyPython
93
104
  rescue Conversion::UnsupportedConversion => exc
94
105
  RubyPyProxy.new pyobject
95
106
  end
107
+ private :_wrap
96
108
 
97
109
  reveal(:respond_to?)
98
110
 
99
- #Moves the old respond_to? method to is_real_method?
111
+ # The standard Ruby +#respond_to?+ method has been renamed to allow
112
+ # RubyPython to query if the proxied \Python object supports the method
113
+ # desired. Setter methods (e.g., +foo=+) are always supported.
100
114
  alias :is_real_method? :respond_to?
101
115
 
102
- #RubyPython checks the attribute dictionary of the wrapped object
103
- #to check whether it will respond to a method call. This should not
104
- #return false positives but it may return false negatives. The builitin Ruby
105
- #respond_to? method has been aliased to is_real_method?.
116
+ # RubyPython checks the attribute dictionary of the wrapped object to
117
+ # check whether it will respond to a method call. This should not return
118
+ # false positives but it may return false negatives. The built-in Ruby
119
+ # respond_to? method has been aliased to is_real_method?.
106
120
  def respond_to?(mname)
107
121
  return true if is_real_method?(mname)
108
122
  mname = mname.to_s
109
- return true if mname.end_with? '='
123
+ return true if mname =~ /=$/
110
124
  @pObject.hasAttr(mname)
111
125
  end
112
126
 
113
- #Implements the method call delegation.
127
+ # Delegates method calls to proxied \Python objects.
128
+ #
129
+ # == Delegation Rules
130
+ # 1. If the method ends with a question-mark (e.g., +nil?+), it can only
131
+ # be a Ruby method on RubyPyProxy. Attempt to reveal it (RubyPyProxy
132
+ # is a BlankObject) and call it.
133
+ # 2. If the method ends with equals signs (e.g., +value=+) it's a setter
134
+ # and we can always set an attribute on a \Python object.
135
+ # 3. If the method ends with an exclamation point (e.g., +foo!+) we are
136
+ # attempting to call a method with keyword arguments.
137
+ # 4. The Python method or value will be called, if it's callable.
138
+ # 5. RubyPython will wrap the return value in a RubyPyProxy object
139
+ # (unless legacy_mode has been turned on).
140
+ # 6. If a block has been provided, the wrapped return value will be
141
+ # passed into the block.
114
142
  def method_missing(name, *args, &block)
115
143
  name = name.to_s
116
144
 
117
- if(name.end_with? "?")
145
+ if name =~ /\?$/
118
146
  begin
119
147
  RubyPyProxy.reveal(name.to_sym)
120
148
  return self.__send__(name.to_sym, *args, &block)
@@ -124,24 +152,17 @@ module RubyPython
124
152
  end
125
153
  end
126
154
 
155
+ kwargs = false
127
156
 
128
- if(name.end_with? "=")
129
- setter = true
130
- name.chomp! "="
131
- else
132
- setter=false
133
- end
134
-
135
- if(!@pObject.hasAttr(name) and !setter)
136
- raise NoMethodError.new(name)
157
+ if name =~ /=$/
158
+ return @pObject.setAttr(name.chomp('='),
159
+ PyObject.convert(*args).first)
160
+ elsif name =~ /!$/
161
+ kwargs = true
162
+ name.chomp! "!"
137
163
  end
138
164
 
139
-
140
- args = PyObject.convert(*args)
141
-
142
- if setter
143
- return @pObject.setAttr(name, args[0])
144
- end
165
+ raise NoMethodError.new(name) if !@pObject.hasAttr(name)
145
166
 
146
167
  pFunc = @pObject.getAttr(name)
147
168
 
@@ -149,77 +170,108 @@ module RubyPython
149
170
  if args.empty? and pFunc.class?
150
171
  pReturn = pFunc
151
172
  else
173
+ if kwargs and args.last.is_a?(Hash)
174
+ pKeywords = PyObject.convert(args.pop).first
175
+ end
176
+
177
+ orig_args = args
178
+ args = PyObject.convert(*args)
152
179
  pTuple = PyObject.buildArgTuple(*args)
153
- pReturn = pFunc.callObject(pTuple)
154
- if(PythonError.error?)
155
- raise PythonError.handle_error
180
+ pReturn = if pKeywords
181
+ pFunc.callObjectKeywords(pTuple, pKeywords)
182
+ else
183
+ pFunc.callObject(pTuple)
184
+ end
185
+
186
+ # Clean up unused Python vars instead of waiting on Ruby's GC to
187
+ # do it.
188
+ pFunc.xDecref
189
+ pTuple.xDecref
190
+ pKeywords.xDecref if pKeywords
191
+ orig_args.each_with_index do |arg, i|
192
+ # Only decref objects that were created in PyObject.convert.
193
+ if !arg.kind_of?(RubyPython::PyObject) and !arg.kind_of?(RubyPython::RubyPyProxy)
194
+ args[i].xDecref
195
+ end
156
196
  end
197
+
198
+ raise PythonError.handle_error if PythonError.error?
157
199
  end
158
200
  else
159
201
  pReturn = pFunc
160
202
  end
161
203
 
162
- return _wrap(pReturn)
204
+ result = _wrap(pReturn)
205
+
206
+ if block
207
+ block.call(result)
208
+ else
209
+ result
210
+ end
163
211
  end
164
212
 
165
- #RubyPython will attempt to translate the wrapped object into a native
166
- #Ruby object. This will only succeed for simple builtin type.
213
+ # RubyPython will attempt to translate the wrapped object into a native
214
+ # Ruby object. This will only succeed for simple built-in type.
167
215
  def rubify
168
216
  @pObject.rubify
169
217
  end
170
218
 
171
- #Returns the string representation of the wrapped object via a call to the
172
- #object's \_\_repr\_\_ method.
173
- #
174
- #@return [String]
219
+ # Returns the String representation of the wrapped object via a call to
220
+ # the object's <tt>__repr__</tt> method, or the +repr+ method in PyMain.
175
221
  def inspect
176
222
  self.__repr__.rubify
177
223
  rescue PythonError, NoMethodError
178
224
  RubyPython::PyMain.repr(self).rubify
179
225
  end
180
226
 
181
- #Returns the string representation of the wrapped object via a call to the
182
- #object's \_\_str\_\_ method.
183
- #
184
- #@return [String]
227
+ # Returns the string representation of the wrapped object via a call to
228
+ # the object's <tt>__str__</tt> method or the +str+ method in PyMain.
185
229
  def to_s
186
230
  self.__str__.rubify
187
231
  rescue PythonError, NoMethodError
188
232
  RubyPython::PyMain.str(self).rubify
189
233
  end
190
234
 
191
- #Converts the wrapped Python object to a Ruby Array. Note that this only converts
192
- #one level, so a nested array will remain a proxy object. Only wrapped
193
- #objects which have an \_\_iter\_\_ method may be converted using to_a.
235
+ # Converts the wrapped \Python object to a Ruby Array. Note that this
236
+ # only converts one level, so a nested array will remain a proxy object.
237
+ # Only wrapped objects which have an <tt>__iter__</tt> method may be
238
+ # converted using +to_a+.
239
+ #
240
+ # Note that for \Python Dict objects, this method returns what you would
241
+ # get in \Python, not in Ruby: +a_dict.to_a+ returns an array of the
242
+ # dictionary's keys.
194
243
  #
195
- #Note that for Dict objects, this method returns what you would get in
196
- #Python, not in Ruby i.e. a\_dict.to\_a returns an array of the
197
- #dictionary's keys.
198
- #@return [Array<RubyPyProxy>]
199
- #@example List
200
- # irb(main):001:0> RubyPython.start
201
- # => true
202
- # irb(main):002:0> a_list = RubyPython::RubyPyProxy.new [1, 'a', 2, 'b']
203
- # => [1, 'a', 2, 'b']
204
- # irb(main):003:0> a_list.kind_of? RubyPython::RubyPyProxy
205
- # => true
206
- # irb(main):004:0> a_list.to_a
207
- # => [1, 'a', 2, 'b']
208
- # irb(main):005:0> RubyPython.stop
209
- # => true
210
- #
211
- #@example Dict
212
- # irb(main):001:0> RubyPython.start
213
- # => true
214
- # irb(main):002:0> a_dict = RubyPython::RubyPyProxy.new({1 => '2', :three => [4,5]})
215
- # => {1: '2', 'three': [4, 5]}
216
- # irb(main):003:0> a_dict.kind_of? RubyPython::RubyPyProxy
217
- # => true
218
- # irb(main):004:0> a_dict.to_a
219
- # => [1, 'three']
220
- # irb(main):005:0> RubyPython.stop
221
- # => true
244
+ # === List #to_a Returns an Array
245
+ # >> RubyPython.start
246
+ # => true
247
+ # >> list = RubyPython::RubyPyProxy.new([1, 'a', 2, 'b'])
248
+ # => [1, 'a', 2, 'b']
249
+ # >> list.kind_of? RubyPython::RubyPyProxy
250
+ # => true
251
+ # >> list.to_a
252
+ # => [1, 'a', 2, 'b']
253
+ # >> RubyPython.stop
254
+ # => true
222
255
  #
256
+ # === Dict #to_a Returns An Array of Keys
257
+ # >> RubyPython.start
258
+ # => true
259
+ # >> dict = RubyPython::RubyPyProxy.new({1 => '2', :three => [4,5]})
260
+ # => {1: '2', 'three': [4, 5]}
261
+ # >> dict.kind_of? RubyPython::RubyPyProxy
262
+ # => true
263
+ # >> dict.to_a
264
+ # => [1, 'three']
265
+ # >> RubyPython.stop
266
+ # => true
267
+ #
268
+ # === Non-Array Values Do Not Convert
269
+ # >> RubyPython.start
270
+ # => true
271
+ # >> item = RubyPython::RubyPyProxy.new(42)
272
+ # => 42
273
+ # >> item.to_a
274
+ # NoMethodError: __iter__
223
275
  def to_a
224
276
  iter = self.__iter__
225
277
  ary = []
@@ -231,30 +283,54 @@ module RubyPython
231
283
  ary
232
284
  end
233
285
 
234
- end
286
+ # Returns the methods on the \Python object by calling the +dir+
287
+ # built-in.
288
+ def methods
289
+ pObject.dir.map { |x| x.to_sym }
290
+ end
235
291
 
236
- #A class to wrap Python Modules. It behaves exactly the same as {RubyPyProxy}.
237
- #It is just here for Bookkeeping and aesthetics.
238
- class RubyPyModule < RubyPyProxy
292
+ # Creates a PyEnumerable for this object. The object must have the
293
+ # <tt>__iter__</tt> method.
294
+ def to_enum
295
+ PyEnumerable.new(@pObject)
296
+ end
239
297
  end
240
298
 
241
- #A class to wrap Python Classes.
242
- class RubyPyClass < RubyPyProxy
299
+ # A class to wrap \Python modules. It behaves exactly the same as
300
+ # RubyPyProxy. It is just here for Bookkeeping and aesthetics.
301
+ class RubyPyModule < RubyPyProxy; end
243
302
 
244
- #Create an instance of the wrapped class. This is a workaround for the fact
245
- #that Python classes are meant to be callable.
303
+ # A class to wrap \Python classes.
304
+ class RubyPyClass < RubyPyProxy
305
+ # Create an instance of the wrapped class. This is a workaround for the
306
+ # fact that \Python classes are meant to be callable.
246
307
  def new(*args)
247
308
  args = PyObject.convert(*args)
248
309
  pTuple = PyObject.buildArgTuple(*args)
249
310
  pReturn = @pObject.callObject(pTuple)
250
- if PythonError.error?
251
- raise PythonError.handle_error
252
- end
311
+ raise PythonError.handle_error if PythonError.error?
253
312
  RubyPyInstance.new pReturn
254
313
  end
255
314
  end
256
315
 
257
- #An object representing an instance of a Python Class.
258
- class RubyPyInstance < RubyPyProxy
316
+ # An object representing an instance of a \Python class. It behaves
317
+ # exactly the same as RubyPyProxy. It is just here for Bookkeeping and
318
+ # aesthetics.
319
+ class RubyPyInstance < RubyPyProxy; end
320
+
321
+ # An object representing a Python enumerable object.
322
+ class PyEnumerable < RubyPyProxy
323
+ include Enumerable
324
+
325
+ def each
326
+ iter = self.__iter__
327
+ loop do
328
+ begin
329
+ yield iter.next
330
+ rescue RubyPython::PythonError => exc
331
+ return if exc.message =~ /StopIteration/
332
+ end
333
+ end
334
+ end
259
335
  end
260
336
  end