rubypython-raspi 0.1.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.
@@ -0,0 +1,195 @@
1
+ require 'ffi'
2
+ require 'thread'
3
+ require 'rubypython/interpreter'
4
+
5
+ module RubyPython
6
+ # This module will hold the loaded RubyPython interpreter.
7
+ module Python #:nodoc: all
8
+ end
9
+ end
10
+
11
+ class RubyPython::Interpreter
12
+ # Infects the provided module with the Python FFI. Once a single module
13
+ # has been infected, the #infect! method is removed from
14
+ # RubyPython::Interpreter.
15
+ def infect!(mod)
16
+ Mutex.new.synchronize do
17
+ self.class.class_eval do
18
+ undef :infect!
19
+ end
20
+
21
+ mod.extend FFI::Library
22
+ # FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL
23
+ mod.ffi_lib_flags :lazy, :global
24
+ mod.ffi_lib self.library
25
+
26
+ # This class is a little bit of a hack to extract the address of
27
+ # global structs. If someone knows a better way please let me know.
28
+ mod.module_eval do
29
+ self.const_set :DummyStruct, Class.new(FFI::Struct)
30
+ self::DummyStruct.layout :dummy_var, :int
31
+
32
+ self.const_set(:PY_FILE_INPUT, 257)
33
+ self.const_set(:PY_EVAL_INPUT, 258)
34
+ self.const_set(:METH_VARARGS, 0x0001)
35
+
36
+ # Function methods & constants
37
+ attach_function :PyCFunction_New, [:pointer, :pointer], :pointer
38
+ callback :PyCFunction, [:pointer, :pointer], :pointer
39
+
40
+ attach_function :PyRun_String, [:string, :int, :pointer, :pointer], :pointer
41
+ attach_function :PyRun_SimpleString, [:string], :pointer
42
+ attach_function :Py_CompileString, [:string, :string, :int], :pointer
43
+ attach_function :PyEval_EvalCode, [:pointer, :pointer, :pointer], :pointer
44
+ attach_function :PyErr_SetString, [:pointer, :string], :void
45
+
46
+ # Python interpreter startup and shutdown
47
+ attach_function :Py_IsInitialized, [], :int
48
+ attach_function :Py_Initialize, [], :void
49
+ attach_function :Py_Finalize, [], :void
50
+
51
+ # Module methods
52
+ attach_function :PyImport_ImportModule, [:string], :pointer
53
+
54
+ # Object Methods
55
+ attach_function :PyObject_HasAttrString, [:pointer, :string], :int
56
+ attach_function :PyObject_GetAttrString, [:pointer, :string], :pointer
57
+ attach_function :PyObject_SetAttrString, [:pointer, :string, :pointer], :int
58
+ attach_function :PyObject_Dir, [:pointer], :pointer
59
+
60
+ attach_function :PyObject_Compare, [:pointer, :pointer], :int
61
+
62
+ attach_function :PyObject_Call, [:pointer, :pointer, :pointer], :pointer
63
+ attach_function :PyObject_CallObject, [:pointer, :pointer], :pointer
64
+ attach_function :PyCallable_Check, [:pointer], :int
65
+
66
+ ### Python To Ruby Conversion
67
+ # String Methods
68
+ attach_function :PyString_AsString, [:pointer], :string
69
+ attach_function :PyString_FromString, [:string], :pointer
70
+ attach_function :PyString_AsStringAndSize, [:pointer, :pointer, :pointer], :int
71
+ attach_function :PyString_FromStringAndSize, [:buffer_in, :ssize_t], :pointer
72
+
73
+ # List Methods
74
+ attach_function :PyList_GetItem, [:pointer, :int], :pointer
75
+ attach_function :PyList_Size, [:pointer], :int
76
+ attach_function :PyList_New, [:int], :pointer
77
+ attach_function :PyList_SetItem, [:pointer, :int, :pointer], :void
78
+
79
+ # Integer Methods
80
+ attach_function :PyInt_AsLong, [:pointer], :long
81
+ attach_function :PyInt_FromLong, [:long], :pointer
82
+
83
+ attach_function :PyLong_AsLong, [:pointer], :long
84
+ attach_function :PyLong_FromLong, [:pointer], :long
85
+
86
+ # Float Methods
87
+ attach_function :PyFloat_AsDouble, [:pointer], :double
88
+ attach_function :PyFloat_FromDouble, [:double], :pointer
89
+
90
+ # Tuple Methods
91
+ attach_function :PySequence_List, [:pointer], :pointer
92
+ attach_function :PySequence_Tuple, [:pointer], :pointer
93
+ attach_function :PyTuple_Pack, [:int, :varargs], :pointer
94
+
95
+ # Dict/Hash Methods
96
+ attach_function :PyDict_Next, [:pointer, :pointer, :pointer, :pointer], :int
97
+ attach_function :PyDict_New, [], :pointer
98
+ attach_function :PyDict_SetItem, [:pointer, :pointer, :pointer], :int
99
+ attach_function :PyDict_Contains, [:pointer, :pointer], :int
100
+ attach_function :PyDict_GetItem, [:pointer, :pointer], :pointer
101
+
102
+ # Error Methods
103
+ attach_variable :PyExc_Exception, self::DummyStruct.by_ref
104
+ attach_variable :PyExc_StopIteration, self::DummyStruct.by_ref
105
+ attach_function :PyErr_SetNone, [:pointer], :void
106
+ attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void
107
+ attach_function :PyErr_Occurred, [], :pointer
108
+ attach_function :PyErr_Clear, [], :void
109
+
110
+ # Reference Counting
111
+ attach_function :Py_IncRef, [:pointer], :void
112
+ attach_function :Py_DecRef, [:pointer], :void
113
+
114
+ # Type Objects
115
+ # attach_variable :PyBaseObject_Type, self::DummyStruct.by_value # built-in 'object'
116
+ # attach_variable :PyBaseString_Type, self::DummyStruct.by_value
117
+ # attach_variable :PyBool_Type, self::DummyStruct.by_value
118
+ # attach_variable :PyBuffer_Type, self::DummyStruct.by_value
119
+ # attach_variable :PyByteArrayIter_Type, self::DummyStruct.by_value
120
+ # attach_variable :PyByteArray_Type, self::DummyStruct.by_value
121
+ attach_variable :PyCFunction_Type, self::DummyStruct.by_value
122
+ # attach_variable :PyCObject_Type, self::DummyStruct.by_value
123
+ # attach_variable :PyCallIter_Type, self::DummyStruct.by_value
124
+ # attach_variable :PyCapsule_Type, self::DummyStruct.by_value
125
+ # attach_variable :PyCell_Type, self::DummyStruct.by_value
126
+ # attach_variable :PyClassMethod_Type, self::DummyStruct.by_value
127
+ attach_variable :PyClass_Type, self::DummyStruct.by_value
128
+ # attach_variable :PyCode_Type, self::DummyStruct.by_value
129
+ # attach_variable :PyComplex_Type, self::DummyStruct.by_value
130
+ # attach_variable :PyDictItems_Type, self::DummyStruct.by_value
131
+ # attach_variable :PyDictIterItem_Type, self::DummyStruct.by_value
132
+ # attach_variable :PyDictIterKey_Type, self::DummyStruct.by_value
133
+ # attach_variable :PyDictIterValue_Type, self::DummyStruct.by_value
134
+ # attach_variable :PyDictKeys_Type, self::DummyStruct.by_value
135
+ # attach_variable :PyDictProxy_Type, self::DummyStruct.by_value
136
+ # attach_variable :PyDictValues_Type, self::DummyStruct.by_value
137
+ attach_variable :PyDict_Type, self::DummyStruct.by_value
138
+ # attach_variable :PyEllipsis_Type, self::DummyStruct.by_value
139
+ # attach_variable :PyEnum_Type, self::DummyStruct.by_value
140
+ # attach_variable :PyFile_Type, self::DummyStruct.by_value
141
+ attach_variable :PyFloat_Type, self::DummyStruct.by_value
142
+ # attach_variable :PyFrame_Type, self::DummyStruct.by_value
143
+ # attach_variable :PyFrozenSet_Type, self::DummyStruct.by_value
144
+ attach_variable :PyFunction_Type, self::DummyStruct.by_value
145
+ # attach_variable :PyGen_Type, self::DummyStruct.by_value
146
+ # attach_variable :PyGetSetDescr_Type, self::DummyStruct.by_value
147
+ # attach_variable :PyInstance_Type, self::DummyStruct.by_value
148
+ attach_variable :PyInt_Type, self::DummyStruct.by_value
149
+ attach_variable :PyList_Type, self::DummyStruct.by_value
150
+ attach_variable :PyLong_Type, self::DummyStruct.by_value
151
+ # attach_variable :PyMemberDescr_Type, self::DummyStruct.by_value
152
+ # attach_variable :PyMemoryView_Type, self::DummyStruct.by_value
153
+ attach_variable :PyMethod_Type, self::DummyStruct.by_value
154
+ # attach_variable :PyModule_Type, self::DummyStruct.by_value
155
+ # attach_variable :PyNullImporter_Type, self::DummyStruct.by_value
156
+ # attach_variable :PyProperty_Type, self::DummyStruct.by_value
157
+ # attach_variable :PyRange_Type, self::DummyStruct.by_value
158
+ # attach_variable :PyReversed_Type, self::DummyStruct.by_value
159
+ # attach_variable :PySTEntry_Type, self::DummyStruct.by_value
160
+ # attach_variable :PySeqIter_Type, self::DummyStruct.by_value
161
+ # attach_variable :PySet_Type, self::DummyStruct.by_value
162
+ # attach_variable :PySlice_Type, self::DummyStruct.by_value
163
+ # attach_variable :PyStaticMethod_Type, self::DummyStruct.by_value
164
+ attach_variable :PyString_Type, self::DummyStruct.by_value
165
+ # attach_variable :PySuper_Type, self::DummyStruct.by_value # built-in 'super'
166
+ # attach_variable :PyTraceBack_Type, self::DummyStruct.by_value
167
+ attach_variable :PyTuple_Type, self::DummyStruct.by_value
168
+ attach_variable :PyType_Type, self::DummyStruct.by_value
169
+ # attach_variable :PyUnicode_Type, self::DummyStruct.by_value
170
+ # attach_variable :PyWrapperDescr_Type, self::DummyStruct.by_value
171
+
172
+ attach_variable :Py_TrueStruct, :_Py_TrueStruct, self::DummyStruct.by_value
173
+ attach_variable :Py_ZeroStruct, :_Py_ZeroStruct, self::DummyStruct.by_value
174
+ attach_variable :Py_NoneStruct, :_Py_NoneStruct, self::DummyStruct.by_value
175
+
176
+ # This is an implementation of the basic structure of a Python PyObject
177
+ # struct. The C struct is actually much larger, but since we only access
178
+ # the first two data members via FFI and always deal with struct
179
+ # pointers there is no need to mess around with the rest of the object.
180
+ self.const_set :PyObjectStruct, Class.new(FFI::Struct)
181
+ self::PyObjectStruct.layout :ob_refcnt, :ssize_t,
182
+ :ob_type, :pointer
183
+
184
+ # This struct is used when defining Python methods.
185
+ self.const_set :PyMethodDef, Class.new(FFI::Struct)
186
+ self::PyMethodDef.layout :ml_name, :pointer,
187
+ :ml_meth, :PyCFunction,
188
+ :ml_flags, :int,
189
+ :ml_doc, :pointer
190
+ end
191
+
192
+ end
193
+ end
194
+ private :infect!
195
+ end
@@ -0,0 +1,80 @@
1
+ require 'rubypython/python'
2
+ require 'rubypython/macros'
3
+
4
+ # Raised when an error occurs in the \Python interpreter.
5
+ class RubyPython::PythonError < RuntimeError
6
+ # The \Python traceback object associated with this error. This will be
7
+ # a RubyPython::RubyPyProxy object.
8
+ attr_reader :traceback
9
+
10
+ # Creates the PythonError.
11
+ # [typeName] The class name of the \Python error.
12
+ # [msg] The message attached to the \Python error.
13
+ # [traceback] The traceback, if any, associated with the \Python error.
14
+ def initialize(typeName, msg, traceback = nil)
15
+ @type = typeName
16
+ @traceback = traceback
17
+ super([typeName, msg].join(': '))
18
+ end
19
+
20
+ # This method should be called when an error has occurred in the \Python
21
+ # interpreter. This acts as factory function for PythonError objects. The
22
+ # function fetches calls +#fetch+ to get the error information from the
23
+ # \Python interpreter and uses this to build a PythonError object. It then
24
+ # calls +#clear to clear the error flag in the python interpreter. After
25
+ # the error flag has been cleared, the PythonError object is returned.
26
+ def self.handle_error
27
+ rbType, rbValue, rbTraceback = fetch()
28
+
29
+ if not rbValue.null?
30
+ msg = rbValue.getAttr("__str__").callObject RubyPython::PyObject.buildArgTuple
31
+ msg = msg.rubify
32
+ else
33
+ msg = nil
34
+ end
35
+
36
+ if not rbTraceback.null?
37
+ traceback = RubyPython::RubyPyProxy.new rbTraceback
38
+ else
39
+ traceback = nil
40
+ end
41
+
42
+ # Decrease the reference count. This will happen anyway when they go out
43
+ # of scope but might as well.
44
+ rbValue.xDecref
45
+ pyName = rbType.getAttr("__name__")
46
+
47
+ rbType.xDecref
48
+ rbName = pyName.rubify
49
+ pyName.xDecref
50
+
51
+ RubyPython::PythonError.clear
52
+ RubyPython::PythonError.new(rbName, msg, traceback)
53
+ end
54
+
55
+ # A wrapper to the \Python C API +PyErr_Fetch+ function. Returns an array
56
+ # with three PyObject instances, representing the Type, the Value, and the
57
+ # stack trace of the Python error.
58
+ def self.fetch
59
+ typePointer = FFI::MemoryPointer.new :pointer
60
+ valuePointer = FFI::MemoryPointer.new :pointer
61
+ tracebackPointer = FFI::MemoryPointer.new :pointer
62
+
63
+ RubyPython::Python.PyErr_Fetch typePointer, valuePointer, tracebackPointer
64
+
65
+ rbType = RubyPython::PyObject.new typePointer.read_pointer
66
+ rbValue = RubyPython::PyObject.new valuePointer.read_pointer
67
+ rbTraceback = RubyPython::PyObject.new tracebackPointer.read_pointer
68
+ [rbType, rbValue, rbTraceback]
69
+ end
70
+
71
+ # Determines whether an error has occurred in the \Python interpreter.
72
+ def self.error?
73
+ !RubyPython::Python.PyErr_Occurred.null?
74
+ end
75
+
76
+ # Resets the \Python interpreter error flag
77
+ def self.clear
78
+ RubyPython::Python.PyErr_Clear
79
+ end
80
+ end
@@ -0,0 +1,336 @@
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. 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.
96
+ def _wrap(pyobject)
97
+ if pyobject.class?
98
+ RubyPyClass.new(pyobject)
99
+ elsif RubyPython.__send__ :legacy_mode?
100
+ pyobject.rubify
101
+ else
102
+ RubyPyProxy.new(pyobject)
103
+ end
104
+ rescue Conversion::UnsupportedConversion => exc
105
+ RubyPyProxy.new pyobject
106
+ end
107
+ private :_wrap
108
+
109
+ reveal(:respond_to?)
110
+
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.
114
+ alias :is_real_method? :respond_to?
115
+
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?.
120
+ def respond_to?(mname)
121
+ return true if is_real_method?(mname)
122
+ mname = mname.to_s
123
+ return true if mname =~ /=$/
124
+ @pObject.hasAttr(mname)
125
+ end
126
+
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.
142
+ def method_missing(name, *args, &block)
143
+ name = name.to_s
144
+
145
+ if name =~ /\?$/
146
+ begin
147
+ RubyPyProxy.reveal(name.to_sym)
148
+ return self.__send__(name.to_sym, *args, &block)
149
+ rescue RuntimeError => exc
150
+ raise NoMethodError.new(name) if exc.message =~ /Don't know how to reveal/
151
+ raise
152
+ end
153
+ end
154
+
155
+ kwargs = false
156
+
157
+ if name =~ /=$/
158
+ return @pObject.setAttr(name.chomp('='),
159
+ PyObject.convert(*args).first)
160
+ elsif name =~ /!$/
161
+ kwargs = true
162
+ name.chomp! "!"
163
+ end
164
+
165
+ raise NoMethodError.new(name) if !@pObject.hasAttr(name)
166
+
167
+ pFunc = @pObject.getAttr(name)
168
+
169
+ if pFunc.callable?
170
+ if args.empty? and pFunc.class?
171
+ pReturn = pFunc
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)
179
+ pTuple = PyObject.buildArgTuple(*args)
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
196
+ end
197
+
198
+ raise PythonError.handle_error if PythonError.error?
199
+ end
200
+ else
201
+ pReturn = pFunc
202
+ end
203
+
204
+ result = _wrap(pReturn)
205
+
206
+ if block
207
+ block.call(result)
208
+ else
209
+ result
210
+ end
211
+ end
212
+
213
+ # RubyPython will attempt to translate the wrapped object into a native
214
+ # Ruby object. This will only succeed for simple built-in type.
215
+ def rubify
216
+ @pObject.rubify
217
+ end
218
+
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.
221
+ def inspect
222
+ self.__repr__.rubify
223
+ rescue PythonError, NoMethodError
224
+ RubyPython::PyMain.repr(self).rubify
225
+ end
226
+
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.
229
+ def to_s
230
+ self.__str__.rubify
231
+ rescue PythonError, NoMethodError
232
+ RubyPython::PyMain.str(self).rubify
233
+ end
234
+
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.
243
+ #
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
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__
275
+ def to_a
276
+ iter = self.__iter__
277
+ ary = []
278
+ loop do
279
+ ary << iter.next()
280
+ end
281
+ rescue PythonError => exc
282
+ raise if exc.message !~ /StopIteration/
283
+ ary
284
+ end
285
+
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
291
+
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
297
+ end
298
+
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
302
+
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.
307
+ def new(*args)
308
+ args = PyObject.convert(*args)
309
+ pTuple = PyObject.buildArgTuple(*args)
310
+ pReturn = @pObject.callObject(pTuple)
311
+ raise PythonError.handle_error if PythonError.error?
312
+ RubyPyInstance.new pReturn
313
+ end
314
+ end
315
+
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
335
+ end
336
+ end