rubypython 0.3.2 → 0.5.0

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