rubypython 0.2.11 → 0.3.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 (72) hide show
  1. data.tar.gz.sig +0 -0
  2. data/{History.txt → History.markdown} +34 -28
  3. data/Manifest.txt +26 -40
  4. data/PostInstall.txt +2 -1
  5. data/README.markdown +103 -0
  6. data/Rakefile +19 -3
  7. data/lib/rubypython.rb +118 -114
  8. data/lib/rubypython/blankobject.rb +21 -0
  9. data/lib/rubypython/conversion.rb +198 -0
  10. data/lib/rubypython/core_ext/string.rb +7 -0
  11. data/lib/rubypython/legacy.rb +15 -0
  12. data/lib/rubypython/macros.rb +47 -0
  13. data/lib/rubypython/operators.rb +111 -0
  14. data/lib/rubypython/pymainclass.rb +51 -0
  15. data/lib/rubypython/pyobject.rb +203 -0
  16. data/lib/rubypython/python.rb +111 -0
  17. data/lib/rubypython/pythonerror.rb +78 -0
  18. data/lib/rubypython/rubypyproxy.rb +214 -0
  19. data/lib/rubypython/version.rb +4 -3
  20. data/spec/conversion_spec.rb +66 -0
  21. data/spec/legacy_spec.rb +22 -0
  22. data/spec/pymainclass_spec.rb +26 -0
  23. data/spec/pyobject_spec.rb +264 -0
  24. data/spec/python_helpers/objects.py +41 -0
  25. data/spec/pythonerror_spec.rb +43 -0
  26. data/spec/refcnt_spec.rb +68 -0
  27. data/spec/rubypyclass_spec.rb +13 -0
  28. data/spec/rubypyproxy_spec.rb +249 -0
  29. data/spec/rubypython_spec.rb +62 -0
  30. data/spec/spec.opts +2 -0
  31. data/spec/spec_helper.rb +51 -0
  32. metadata +79 -73
  33. metadata.gz.sig +0 -0
  34. data/README.txt +0 -60
  35. data/ext/rubypython_bridge/cbridge.c +0 -150
  36. data/ext/rubypython_bridge/cbridge.h +0 -15
  37. data/ext/rubypython_bridge/config.h +0 -14
  38. data/ext/rubypython_bridge/extconf.rb +0 -43
  39. data/ext/rubypython_bridge/ptor.c +0 -242
  40. data/ext/rubypython_bridge/ptor.h +0 -15
  41. data/ext/rubypython_bridge/rp_blankobject.c +0 -42
  42. data/ext/rubypython_bridge/rp_blankobject.h +0 -11
  43. data/ext/rubypython_bridge/rp_class.c +0 -56
  44. data/ext/rubypython_bridge/rp_class.h +0 -7
  45. data/ext/rubypython_bridge/rp_error.c +0 -34
  46. data/ext/rubypython_bridge/rp_error.h +0 -11
  47. data/ext/rubypython_bridge/rp_function.c +0 -31
  48. data/ext/rubypython_bridge/rp_function.h +0 -7
  49. data/ext/rubypython_bridge/rp_instance.c +0 -164
  50. data/ext/rubypython_bridge/rp_instance.h +0 -7
  51. data/ext/rubypython_bridge/rp_module.c +0 -160
  52. data/ext/rubypython_bridge/rp_module.h +0 -8
  53. data/ext/rubypython_bridge/rp_object.c +0 -194
  54. data/ext/rubypython_bridge/rp_object.h +0 -23
  55. data/ext/rubypython_bridge/rp_util.c +0 -63
  56. data/ext/rubypython_bridge/rp_util.h +0 -11
  57. data/ext/rubypython_bridge/rtop.c +0 -212
  58. data/ext/rubypython_bridge/rtop.h +0 -17
  59. data/ext/rubypython_bridge/rubypython_bridge.c +0 -125
  60. data/ext/rubypython_bridge/rubypython_bridge.h +0 -10
  61. data/lib/rubypython/session.rb +0 -4
  62. data/lib/rubypython/wrapper_extensions.rb +0 -83
  63. data/setup.rb +0 -1585
  64. data/tasks/environment.rake +0 -7
  65. data/tasks/extconf.rake +0 -13
  66. data/tasks/extconf/rubypython_bridge.rake +0 -49
  67. data/test/python_helpers/objects.py +0 -12
  68. data/test/test.wav +0 -0
  69. data/test/test_helper.rb +0 -2
  70. data/test/test_rubypython.rb +0 -215
  71. data/test/test_rubypython_bridge_extn.rb +0 -133
  72. data/test/test_session.rb +0 -6
@@ -0,0 +1,203 @@
1
+ require 'rubypython/python'
2
+ require 'rubypython/macros'
3
+ require 'rubypython/conversion'
4
+ require 'ffi'
5
+
6
+ module RubyPython
7
+ #This object is an opaque wrapper around the C PyObject\* type used by the
8
+ #python C API. This class **should not** be used by the end user. They
9
+ #should instead make use of the {RubyPyProxy} class and its
10
+ #subclasses.
11
+ class PyObject
12
+
13
+ #@private
14
+ #
15
+ #This class wraps C PyObject\*s so that the Python reference count is
16
+ #automatically decreased when the Ruby object referencing them
17
+ #goes out of scope.
18
+ class AutoPyPointer < FFI::AutoPointer
19
+ class << self
20
+ #Keeps track of which objects are associated with the currently running
21
+ #Python interpreter, so that RubyPython knows not to try to decrease the
22
+ #reference counts of the others when garbage collecting.
23
+ attr_accessor :current_pointers
24
+
25
+ #When used along with the FFI Library method is executed whenever a
26
+ #pointer is garbage collected so that cleanup can be done. In our case
27
+ #we decrease the reference count of the held pointer as long as the
28
+ #object is still good. There is really no reason the end-user would need
29
+ #to the use this method directly.
30
+ def release(pointer)
31
+ obj_id = pointer.object_id
32
+ if (Python.Py_IsInitialized != 0) and @current_pointers.delete(obj_id)
33
+ Python.Py_DecRef pointer
34
+ end
35
+ end
36
+ end
37
+
38
+ self.current_pointers = {}
39
+ end
40
+
41
+ #The FFI::Pointer object which represents the Python PyObject\*.
42
+ attr_reader :pointer
43
+
44
+ #@param [FFI::Pointer, other] pointer objects passed in to the constructor
45
+ # are just assigned to the pointer attribute of the instance. All other
46
+ # objects are converted via {Conversion.rtopObject} before being assigned.
47
+ def initialize(rObject)
48
+ if rObject.kind_of? FFI::AutoPointer
49
+ new_pointer = FFI::Pointer.new rObject
50
+ @pointer = AutoPyPointer.new new_pointer
51
+ xIncref
52
+ elsif rObject.kind_of? FFI::Pointer
53
+ @pointer = AutoPyPointer.new rObject
54
+ else
55
+ @pointer = AutoPyPointer.new Conversion.rtopObject(rObject)
56
+ end
57
+ AutoPyPointer.current_pointers[@pointer.object_id] = true
58
+ end
59
+
60
+ #Attempts to convert the wrapped object to a native ruby type.
61
+ #@return a ruby version of the wrapped object
62
+ def rubify
63
+ Conversion.ptorObject @pointer
64
+ end
65
+
66
+ #Tests whether the wrapped object has a given attribute
67
+ #@param [String] the name of the attribute to look up
68
+ #@return [Boolean]
69
+ def hasAttr(attrName)
70
+ Python.PyObject_HasAttrString(@pointer, attrName) == 1
71
+ end
72
+
73
+ #Retrieves an object from the wrapped python object
74
+ #@param [String] the name of attribute to fetch
75
+ #@return [PyObject] a Ruby wrapper around the fetched attribute
76
+ def getAttr(attrName)
77
+ pyAttr = Python.PyObject_GetAttrString @pointer, attrName
78
+ self.class.new pyAttr
79
+ end
80
+
81
+ #Sets the an attribute of the wrapped Python object
82
+ #@param [String] attrName the name of of attribute to set
83
+ #@param [PyObject] rbPyAttr a {PyObject} wrapper around the value we wish to
84
+ #set the attribute to.
85
+ #@return [Boolean] returns true if the attribute is sucessfully set.
86
+ def setAttr(attrName, rbPyAttr)
87
+ Python.PyObject_SetAttrString(@pointer, attrName, rbPyAttr.pointer) != -1
88
+ end
89
+
90
+ #Calls the wrapped Python object with the supplied arguments.
91
+ #@param [PyObject] rbPyArgs a {PyObject} wrapping a tuple of the supplied
92
+ #arguments
93
+ #@return [PyObject] a {PyObject} wrapper around the returned
94
+ #object (this may be NULL).
95
+ def callObject(rbPyArgs)
96
+ pyReturn = Python.PyObject_CallObject(@pointer, rbPyArgs.pointer)
97
+ self.class.new pyReturn
98
+ end
99
+
100
+ #Decrease the reference count of the wrapped object
101
+ #@return [void]
102
+ def xDecref
103
+ AutoPyPointer.release(@pointer)
104
+ @pointer.free
105
+ end
106
+
107
+ #Increase the reference count of the wrapped object
108
+ #@return [void]
109
+ def xIncref
110
+ Python.Py_IncRef @pointer
111
+ end
112
+
113
+ #Tests whether the wrapped object is NULL.
114
+ def null?
115
+ @pointer.null?
116
+ end
117
+
118
+ #@return [Number]
119
+ def cmp(other)
120
+ Python.PyObject_Compare @pointer, other.pointer
121
+ end
122
+
123
+ #Tests whether the wrapped object is a function or a method. This is not the
124
+ #same as {#callable?} as many other Python objects are callable.
125
+ def function_or_method?
126
+ isFunc = (Macros.PyObject_TypeCheck(@pointer, Python.PyFunction_Type.to_ptr) != 0)
127
+ isMethod = (Macros.PyObject_TypeCheck(@pointer, Python.PyMethod_Type.to_ptr) != 0)
128
+ isFunc or isMethod
129
+ end
130
+
131
+ #Is the wrapped object callable?
132
+ def callable?
133
+ Python.PyCallable_Check(@pointer) != 0
134
+ end
135
+
136
+ #Tests whether the wrapped object is a Python class (both new and old
137
+ #style).
138
+ def class?
139
+ isClassObj = (Macros.PyObject_TypeCheck(@pointer, Python.PyClass_Type.to_ptr) == 1)
140
+ isTypeObj = (Macros.PyObject_TypeCheck(@pointer, Python.PyType_Type.to_ptr) == 1)
141
+ isTypeObj or isClassObj
142
+ end
143
+
144
+ #Manipulates the supplied {PyObject} instance such that it is suitable to
145
+ #passed to {#callObject}. If `rbObject` is a tuple then the argument passed
146
+ #in is returned. If it is a list then the list is converted to a tuple.
147
+ #Otherwise returns a tuple with one element: `rbObject`.
148
+ #@param [PyObject] rbObject the argment to be turned into a tuple.
149
+ #@return [PyObject<tuple>]
150
+ def self.makeTuple(rbObject)
151
+ pTuple = nil
152
+
153
+ if Macros.PyObject_TypeCheck(rbObject.pointer, Python.PyList_Type.to_ptr) != 0
154
+ pTuple = Python.PySequence_Tuple(rbObject.pointer)
155
+ elsif Macros.PyObject_TypeCheck(rbObject.pointer, Python.PyTuple_Type.to_ptr) != 0
156
+ pTuple = rbObject.pointer
157
+ else
158
+ pTuple = Python.PyTuple_Pack(1, :pointer, rbObject.pointer)
159
+ end
160
+
161
+ self.new pTuple
162
+ end
163
+
164
+ #Wraps up the supplied arguments in Python list.
165
+ #@return [PyObject<list>]
166
+ def self.newList(*args)
167
+ rbList = self.new Python.PyList_New(args.length)
168
+
169
+ args.each_with_index do |el, i|
170
+ Python.PyList_SetItem rbList.pointer, i, el.pointer
171
+ end
172
+
173
+ rbList
174
+ end
175
+
176
+ #Converts the supplied arguments to PyObject instances.
177
+ #@return [Array<PyObject>]
178
+ def self.convert(*args)
179
+ args.map! do |arg|
180
+ if arg.kind_of? PyObject
181
+ arg
182
+ elsif arg.kind_of? RubyPyProxy
183
+ arg.pObject
184
+ else
185
+ PyObject.new arg
186
+ end
187
+ end
188
+ end
189
+
190
+ #Takes an array of wrapped Python objects and wraps them in a tuple such
191
+ #that they may be passed to {#callObject}.
192
+ #@param [Array<PyObject>] args the arguments to be inserted into the tuple.
193
+ #@return [PyObject<tuple>]
194
+ def self.buildArgTuple(*args)
195
+ pList = PyObject.newList(*args)
196
+ pTuple = PyObject.makeTuple(pList)
197
+ pList.xDecref
198
+ pTuple
199
+ end
200
+
201
+ end
202
+
203
+ end
@@ -0,0 +1,111 @@
1
+ require 'ffi'
2
+ require 'open3'
3
+
4
+ module RubyPython
5
+ #This module provides access to the Python C API functions via the Ruby ffi
6
+ #gem. Documentation for these functions may be found [here](http://docs.python.org/c-api/). Likewise the FFI gem documentation may be found [here](http://rdoc.info/projects/ffi/ffi).
7
+ module Python
8
+ extend FFI::Library
9
+ PYTHON_VERSION = Open3.popen3("python --version") { |i,o,e| e.read}.chomp.split[1].to_f
10
+ PYTHON_NAME = "python#{PYTHON_VERSION}"
11
+ LIB_NAME = "lib#{PYTHON_NAME}"
12
+ LIB_EXT = FFI::Platform::LIBSUFFIX
13
+ LIB = File.join(`python -c "import sys; print(sys.prefix)"`.chomp,
14
+ "lib", "#{PYTHON_NAME}", "config", "#{LIB_NAME}.#{LIB_EXT}")
15
+ @ffi_libs = [FFI::DynamicLibrary.open(LIB, FFI::DynamicLibrary::RTLD_LAZY|FFI::DynamicLibrary::RTLD_GLOBAL)]
16
+
17
+ #The class is a little bit of a hack to extract the address of global
18
+ #structs. If someone knows a better way please let me know.
19
+ class DummyStruct < FFI::Struct
20
+ layout :dummy_var, :int
21
+ end
22
+
23
+ #Python interpreter startup and shutdown
24
+ attach_function :Py_IsInitialized, [], :int
25
+ attach_function :Py_Initialize, [], :void
26
+ attach_function :Py_Finalize, [], :void
27
+
28
+ #Module methods
29
+ attach_function :PyImport_ImportModule, [:string], :pointer
30
+
31
+ #Object Methods
32
+ attach_function :PyObject_HasAttrString, [:pointer, :string], :int
33
+ attach_function :PyObject_GetAttrString, [:pointer, :string], :pointer
34
+ attach_function :PyObject_SetAttrString, [:pointer, :string, :pointer], :int
35
+
36
+ attach_function :PyObject_Compare, [:pointer, :pointer], :int
37
+
38
+ attach_function :PyObject_CallObject, [:pointer, :pointer], :pointer
39
+ attach_function :PyCallable_Check, [:pointer], :int
40
+
41
+ ###Python To Ruby Conversion
42
+ #String Methods
43
+ attach_function :PyString_AsString, [:pointer], :string
44
+ attach_function :PyString_FromString, [:string], :pointer
45
+
46
+ #List Methods
47
+ attach_function :PyList_GetItem, [:pointer, :int], :pointer
48
+ attach_function :PyList_Size, [:pointer], :int
49
+ attach_function :PyList_New, [:int], :pointer
50
+ attach_function :PyList_SetItem, [:pointer, :int, :pointer], :void
51
+
52
+ #Integer Methods
53
+ attach_function :PyInt_AsLong, [:pointer], :long
54
+ attach_function :PyInt_FromLong, [:long], :pointer
55
+
56
+ attach_function :PyLong_AsLong, [:pointer], :long
57
+ attach_function :PyLong_FromLong, [:pointer], :long
58
+
59
+ #Float Methods
60
+ attach_function :PyFloat_AsDouble, [:pointer], :double
61
+ attach_function :PyFloat_FromDouble, [:double], :pointer
62
+
63
+ #Tuple Methods
64
+ attach_function :PySequence_List, [:pointer], :pointer
65
+ attach_function :PySequence_Tuple, [:pointer], :pointer
66
+ attach_function :PyTuple_Pack, [:int, :varargs], :pointer
67
+
68
+ #Dict/Hash Methods
69
+ attach_function :PyDict_Next, [:pointer, :pointer, :pointer, :pointer], :int
70
+ attach_function :PyDict_New, [], :pointer
71
+ attach_function :PyDict_SetItem, [:pointer, :pointer, :pointer], :int
72
+ attach_function :PyDict_Contains, [:pointer, :pointer], :int
73
+ attach_function :PyDict_GetItem, [:pointer, :pointer], :pointer
74
+
75
+ #Error Methods
76
+ attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void
77
+ attach_function :PyErr_Occurred, [], :pointer
78
+ attach_function :PyErr_Clear, [], :void
79
+
80
+ #Reference Counting
81
+ attach_function :Py_IncRef, [:pointer], :void
82
+ attach_function :Py_DecRef, [:pointer], :void
83
+
84
+ #Type Objects
85
+ attach_variable :PyString_Type, DummyStruct.by_value
86
+ attach_variable :PyList_Type, DummyStruct.by_value
87
+ attach_variable :PyInt_Type, DummyStruct.by_value
88
+ attach_variable :PyLong_Type, DummyStruct.by_value
89
+ attach_variable :PyFloat_Type, DummyStruct.by_value
90
+ attach_variable :PyTuple_Type, DummyStruct.by_value
91
+ attach_variable :PyDict_Type, DummyStruct.by_value
92
+ attach_variable :PyFunction_Type, DummyStruct.by_value
93
+ attach_variable :PyMethod_Type, DummyStruct.by_value
94
+ attach_variable :PyType_Type, DummyStruct.by_value
95
+ attach_variable :PyClass_Type, DummyStruct.by_value
96
+
97
+ attach_variable :Py_TrueStruct, :_Py_TrueStruct, DummyStruct.by_value
98
+ attach_variable :Py_ZeroStruct, :_Py_ZeroStruct, DummyStruct.by_value
99
+ attach_variable :Py_NoneStruct, :_Py_NoneStruct, DummyStruct.by_value
100
+
101
+ #This is an implementation of the basic structure of a Python PyObject
102
+ #struct. The C struct is actually much larger, but since we only access
103
+ #the first two data members via FFI and always deal with struct pointers
104
+ #there is no need to mess around with the rest of the object.
105
+ class PyObjectStruct < FFI::Struct
106
+ layout :ob_refcnt, :int,
107
+ :ob_type, :pointer
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,78 @@
1
+ require 'rubypython/python'
2
+ require 'rubypython/macros'
3
+
4
+
5
+ module RubyPython
6
+
7
+ #Raised when an error occurs in the Python interpreter.
8
+ class PythonError < Exception
9
+
10
+ #@param [String] typeName the class name of the Python error
11
+ #@param [String] msg the message attached to the Python error
12
+ def initialize(typeName, msg)
13
+ @type = typeName
14
+ super([typeName, msg].join(': '))
15
+ end
16
+
17
+ #This method should be called when an error has occured in the
18
+ #Python interpreter. This acts as factory function for PythonError
19
+ #objects. The function fetchs calls {fetch} to get the error
20
+ #information from the Python interpreter and uses this to build
21
+ #a PythonError object. It then calls {clear} to clear the error
22
+ #flag of the python interpreter
23
+ #@return [PythonError] an error enscapsulating the Python error
24
+ def self.handle_error
25
+ rbType, rbValue, rbTraceback = fetch()
26
+
27
+ if not rbValue.null?
28
+ msg = rbValue.getAttr("__str__").callObject PyObject.buildArgTuple
29
+ msg = msg.rubify
30
+ else
31
+ msg = nil
32
+ end
33
+
34
+ #Decrease the reference count. This will happen anyway when they go
35
+ #out of scope but might as well.
36
+ rbValue.xDecref
37
+ rbTraceback.xDecref
38
+ pyName = rbType.getAttr("__name__")
39
+
40
+ rbType.xDecref
41
+ rbName = pyName.rubify
42
+ pyName.xDecref
43
+
44
+ PythonError.clear
45
+
46
+ PythonError.new(rbName, msg)
47
+ end
48
+
49
+ #A wrapper to the Python C API PyErr_Fetch function.
50
+ #@return [Array<PyObject>] an array containing three {PyObject} instances.
51
+ # representing the Type, Value, and stacktrace of the python
52
+ # error respectively.
53
+ def self.fetch
54
+ typePointer = FFI::MemoryPointer.new :pointer
55
+ valuePointer = FFI::MemoryPointer.new :pointer
56
+ tracebackPointer = FFI::MemoryPointer.new :pointer
57
+
58
+ Python.PyErr_Fetch typePointer, valuePointer, tracebackPointer
59
+
60
+ rbType = PyObject.new typePointer.read_pointer
61
+ rbValue = PyObject.new valuePointer.read_pointer
62
+ rbTraceback = PyObject.new tracebackPointer.read_pointer
63
+ [rbType, rbValue, rbTraceback]
64
+ end
65
+
66
+ #Determines whether an error has occured in the python interpreter.
67
+ def self.error?
68
+ !Python.PyErr_Occurred.null?
69
+ end
70
+
71
+ #Resets the Python interpreter error flag
72
+ #@return [void]
73
+ def self.clear
74
+ Python.PyErr_Clear
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,214 @@
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
+ #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.
13
+ #
14
+ #Note: All RubyPyProxy objects become invalid when the Python interpreter
15
+ #is halted.
16
+ class RubyPyProxy < BlankObject
17
+ include Operators
18
+
19
+ attr_reader :pObject
20
+
21
+ def initialize(pObject)
22
+ if pObject.kind_of? PyObject
23
+ @pObject = pObject
24
+ else
25
+ @pObject = PyObject.new pObject
26
+ end
27
+ end
28
+
29
+ #Handles the job of wrapping up anything returned by a {RubyPyProxy}
30
+ #instance. The behavior differs depending on the value of
31
+ #{RubyPython.legacy_mode}. If legacy mode is inactive, every returned
32
+ #object is wrapped by an instance of {RubyPyProxy}. If legacy mode is
33
+ #active, RubyPython first attempts to convert the returned object to a
34
+ #native Ruby type, and then only wraps the object if this fails.
35
+ def _wrap(pyobject)
36
+ if pyobject.class?
37
+ RubyPyClass.new(pyobject)
38
+ elsif RubyPython.legacy_mode
39
+ pyobject.rubify
40
+ else
41
+ RubyPyProxy.new(pyobject)
42
+ end
43
+ rescue Conversion::UnsupportedConversion => exc
44
+ RubyPyProxy.new pyobject
45
+ end
46
+
47
+ reveal(:respond_to?)
48
+
49
+ #Moves the old respond_to? method to is_real_method?
50
+ alias :is_real_method? :respond_to?
51
+
52
+ #RubyPython checks the attribute dictionary of the wrapped object
53
+ #to check whether it will respond to a method call. This should not
54
+ #return false positives but it may return false negatives. The builitin Ruby
55
+ #respond_to? method has been aliased to is_real_method?.
56
+ def respond_to?(mname)
57
+ return true if is_real_method?(mname)
58
+ mname = mname.to_s
59
+ return true if mname.end_with? '='
60
+ @pObject.hasAttr(mname)
61
+ end
62
+
63
+ #Implements the method call delegation.
64
+ def method_missing(name, *args, &block)
65
+ name = name.to_s
66
+
67
+ if(name.end_with? "?")
68
+ begin
69
+ RubyPyProxy.reveal(name.to_sym)
70
+ return self.__send__(name.to_sym, *args, &block)
71
+ rescue RuntimeError => exc
72
+ raise NoMethodError.new(name) if exc.message =~ /Don't know how to reveal/
73
+ raise
74
+ end
75
+ end
76
+
77
+
78
+ if(name.end_with? "=")
79
+ setter = true
80
+ name.chomp! "="
81
+ else
82
+ setter=false
83
+ end
84
+
85
+ if(!@pObject.hasAttr(name) and !setter)
86
+ raise NoMethodError.new(name)
87
+ end
88
+
89
+
90
+ args = PyObject.convert(*args)
91
+
92
+ if setter
93
+ return @pObject.setAttr(name, args[0])
94
+ end
95
+
96
+ pFunc = @pObject.getAttr(name)
97
+
98
+ if pFunc.callable?
99
+ if args.empty? and pFunc.class?
100
+ pReturn = pFunc
101
+ else
102
+ pTuple = PyObject.buildArgTuple(*args)
103
+ pReturn = pFunc.callObject(pTuple)
104
+ if(PythonError.error?)
105
+ raise PythonError.handle_error
106
+ end
107
+ end
108
+ else
109
+ pReturn = pFunc
110
+ end
111
+
112
+ return _wrap(pReturn)
113
+ end
114
+
115
+ #RubyPython will attempt to translate the wrapped object into a native
116
+ #Ruby object. This will only succeed for simple builtin type.
117
+ def rubify
118
+ @pObject.rubify
119
+ end
120
+
121
+ #Returns the string representation of the wrapped object via a call to the
122
+ #object's \_\_repr\_\_ method. Falls back on the default Ruby behavior when
123
+ #this method cannot be found.
124
+ #
125
+ #@return [String]
126
+ def inspect
127
+ self.__repr__.rubify rescue _inspect
128
+ rescue
129
+ class << self; define_method :_inspect, RubyPyProxy.find_hidden_method(:inspect); end
130
+ _inspect
131
+ end
132
+
133
+ #Returns the string representation of the wrapped object via a call to the
134
+ #object's \_\_str\_\_ method. Falls back on the default Ruby behavior when
135
+ #this method cannot be found.
136
+ #
137
+ #@return [String]
138
+ def to_s
139
+ self.__str__.rubify rescue _to_s
140
+ rescue
141
+ class << self; define_method :_to_s, RubyPyProxy.find_hidden_method(:to_s); end
142
+ _to_s
143
+ end
144
+
145
+ #Converts the wrapped Python object to a Ruby Array. Note that this only converts
146
+ #one level, so a nested array will remain a proxy object. Only wrapped
147
+ #objects which have an \_\_iter\_\_ method may be converted using to_a.
148
+ #
149
+ #Note that for Dict objects, this method returns what you would get in
150
+ #Python, not in Ruby i.e. a_dict.to_a returns an array of the
151
+ #dictionary's keys.
152
+ #@return [Array<RubyPyProxy>]
153
+ #@example List
154
+ # irb(main):001:0> RubyPython.start
155
+ # => true
156
+ # irb(main):002:0> a_list = RubyPython::RubyPyProxy.new [1, 'a', 2, 'b']
157
+ # => [1, 'a', 2, 'b']
158
+ # irb(main):003:0> a_list.kind_of? RubyPython::RubyPyProxy
159
+ # => true
160
+ # irb(main):004:0> a_list.to_a
161
+ # => [1, 'a', 2, 'b']
162
+ # irb(main):005:0> RubyPython.stop
163
+ # => true
164
+ #
165
+ #@example Dict
166
+ # irb(main):001:0> RubyPython.start
167
+ # => true
168
+ # irb(main):002:0> a_dict = RubyPython::RubyPyProxy.new({1 => '2', :three => [4,5]})
169
+ # => {1: '2', 'three': [4, 5]}
170
+ # irb(main):003:0> a_dict.kind_of? RubyPython::RubyPyProxy
171
+ # => true
172
+ # irb(main):004:0> a_dict.to_a
173
+ # => [1, 'three']
174
+ # irb(main):005:0> RubyPython.stop
175
+ # => true
176
+
177
+ def to_a
178
+ iter = self.__iter__
179
+ ary = []
180
+ loop do
181
+ ary << iter.next()
182
+ end
183
+ rescue PythonError => exc
184
+ raise if exc.message !~ /StopIteration/
185
+ ary
186
+ end
187
+
188
+ end
189
+
190
+ #A class to wrap Python Modules. It behaves exactly the same as {RubyPyProxy}.
191
+ #It is just here for Bookkeeping and aesthetics.
192
+ class RubyPyModule < RubyPyProxy
193
+ end
194
+
195
+ #A class to wrap Python Classes.
196
+ class RubyPyClass < RubyPyProxy
197
+
198
+ #Create an instance of the wrapped class. This is a workaround for the fact
199
+ #that Python classes are meant to be callable.
200
+ def new(*args)
201
+ args = PyObject.convert(*args)
202
+ pTuple = PyObject.buildArgTuple(*args)
203
+ pReturn = @pObject.callObject(pTuple)
204
+ if PythonError.error?
205
+ raise PythonError.handle_error
206
+ end
207
+ RubyPyInstance.new pReturn
208
+ end
209
+ end
210
+
211
+ #An object representing an instance of a Python Class.
212
+ class RubyPyInstance < RubyPyProxy
213
+ end
214
+ end