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
@@ -1,21 +1,23 @@
1
1
  require 'blankslate'
2
2
 
3
- module RubyPython
4
- #An object to be used as a base class for Proxy classes.
5
- #It is necessary to define this because no such class exists in Ruby
6
- #1.8.x
7
- class BlankObject < BlankSlate
8
- class << self
9
- def hide(name)
10
- if instance_methods.include?(name) and
11
- name.to_s !~ /^(__|instance_eval|object_id)/
3
+ # This document is the basis of the RubyPyProxy precisely because it hides
4
+ # the implementation of so many things that should be forwarded on to the
5
+ # Python object. This class is for internal use only.
6
+ #
7
+ # Note that in Ruby 1.9, BasicObject might be a better choice, but there are
8
+ # some decisions made in the rest of the library that make this harder. I
9
+ # don't see a clean way to integrate both Ruby 1.8 and 1.9 support for this.
10
+ class RubyPython::BlankObject < ::BlankSlate #:nodoc:
11
+ class << self
12
+ def hide(name)
13
+ if instance_methods.include?(name) and
14
+ name.to_s !~ /^(__|instance_eval|object_id)/
12
15
  @hidden_methods ||= {}
13
- @hidden_methods[name.to_sym] = instance_method(name)
14
- undef_method name
15
- end
16
+ @hidden_methods[name.to_sym] = instance_method(name)
17
+ undef_method name
16
18
  end
17
19
  end
18
-
19
- instance_methods.each { |m| hide(m) }
20
20
  end
21
+
22
+ instance_methods.each { |m| hide(m) }
21
23
  end
@@ -1,215 +1,284 @@
1
1
  require 'rubypython/python'
2
2
  require 'rubypython/macros'
3
- module RubyPython
4
- #This modules encapsulates the work of converting between native Ruby and
5
- #Python types. Unsupported conversions raise {UnsupportedConversion}.
6
- module Conversion
7
3
 
8
- #Raised when RubyPython does not know how to convert an object from Python
9
- #to Ruby or vice versa
10
- class UnsupportedConversion < Exception; end
4
+ # Acts as a namespace for methods to bidirectionally convert between native
5
+ # Ruby types and native \Python types. Unsupported conversions raise
6
+ # UnsupportedConversion.
7
+ #
8
+ # The methods in this module should be considered internal implementation to
9
+ # RubyPython as they all return FFI pointers to \Python objects.
10
+ module RubyPython::Conversion
11
+ # Raised when RubyPython does not know how to convert an object from
12
+ # \Python to Ruby or vice versa.
13
+ class UnsupportedConversion < Exception; end
11
14
 
12
- def self.rtopString(rString)
13
- Python.PyString_FromString(rString)
14
- end
15
+ # Convert a Ruby string to a \Python string. Returns an FFI::Pointer to
16
+ # a PyStringObject.
17
+ def self.rtopString(rString)
18
+ size = rString.respond_to?(:bytesize) ? rString.bytesize : rString.size
19
+ RubyPython::Python.PyString_FromStringAndSize(rString, size)
20
+ end
15
21
 
16
- def self.rtopArrayToList(rArray)
17
- size = rArray.length
18
- pList = Python.PyList_New size
19
- rArray.each_with_index do |el, i|
20
- Python.PyList_SetItem pList, i, rtopObject(el)
21
- end
22
- pList
22
+ # Convert a Ruby Array to \Python List. Returns an FFI::Pointer to
23
+ # a PyListObject.
24
+ def self.rtopArrayToList(rArray)
25
+ size = rArray.length
26
+ pList = RubyPython::Python.PyList_New size
27
+ rArray.each_with_index do |el, i|
28
+ RubyPython::Python.PyList_SetItem pList, i, rtopObject(el)
23
29
  end
30
+ pList
31
+ end
24
32
 
25
- def self.rtopArrayToTuple(rArray)
26
- pList = rtopArrayToList(rArray)
27
- pTuple = Python.PySequence_Tuple(pList)
28
- Python.Py_DecRef(pList)
29
- pTuple
30
- end
33
+ # Convert a Ruby Array to \Python Tuple. Returns an FFI::Pointer to a
34
+ # PyTupleObject.
35
+ def self.rtopArrayToTuple(rArray)
36
+ pList = rtopArrayToList(rArray)
37
+ pTuple = RubyPython::Python.PySequence_Tuple(pList)
38
+ RubyPython::Python.Py_DecRef(pList)
39
+ pTuple
40
+ end
31
41
 
32
- def self.rtopHash(rHash)
33
- pDict = Python.PyDict_New
34
- rHash.each do |k,v|
35
- Python.PyDict_SetItem pDict, rtopObject(k, key=true), rtopObject(v)
36
- end
37
- pDict
42
+ # Convert a Ruby Hash to a \Python Dict. Returns an FFI::Pointer to a
43
+ # PyDictObject.
44
+ def self.rtopHash(rHash)
45
+ pDict = RubyPython::Python.PyDict_New
46
+ rHash.each do |k,v|
47
+ RubyPython::Python.PyDict_SetItem pDict, rtopObject(k, key = true),
48
+ rtopObject(v)
38
49
  end
50
+ pDict
51
+ end
39
52
 
40
- def self.rtopFixnum(rNum)
41
- Python.PyInt_FromLong(rNum)
42
- end
53
+ # Convert a Ruby Fixnum to a \Python Int. Returns an FFI::Pointer to a
54
+ # PyIntObject.
55
+ def self.rtopFixnum(rNum)
56
+ RubyPython::Python.PyInt_FromLong(rNum)
57
+ end
43
58
 
44
- def self.rtopBigNum(rNum)
45
- Python.PyLong_FromLong(rNum)
46
- end
59
+ # Convert a Ruby Bignum to a \Python Long. Returns an FFI::Pointer to a
60
+ # PyLongObject.
61
+ def self.rtopBigNum(rNum)
62
+ RubyPython::Python.PyLong_FromLong(rNum)
63
+ end
47
64
 
48
- def self.rtopFloat(rNum)
49
- Python.PyFloat_FromDouble(rNum)
50
- end
65
+ # Convert a Ruby float to a \Python Float. Returns an FFI::Pointer to a
66
+ # PyFloatObject.
67
+ def self.rtopFloat(rNum)
68
+ RubyPython::Python.PyFloat_FromDouble(rNum)
69
+ end
51
70
 
52
- def self.rtopFalse
53
- Macros.Py_RETURN_FALSE
54
- end
71
+ # Returns a \Python False value (equivalent to Ruby's +false+). Returns an
72
+ # FFI::Pointer to Py_ZeroStruct.
73
+ def self.rtopFalse
74
+ RubyPython::Macros.Py_RETURN_FALSE
75
+ end
55
76
 
56
- def self.rtopTrue
57
- Macros.Py_RETURN_TRUE
58
- end
77
+ # Returns a \Python True value (equivalent to Ruby's +true+). Returns an
78
+ # FFI::Pointer to Py_TrueStruct.
79
+ def self.rtopTrue
80
+ RubyPython::Macros.Py_RETURN_TRUE
81
+ end
59
82
 
60
- def self.rtopNone
61
- Macros.Py_RETURN_NONE
62
- end
83
+ # Returns a \Python None value (equivalent to Ruby's +nil+). Returns an
84
+ # FFI::Pointer to Py_NoneStruct.
85
+ def self.rtopNone
86
+ RubyPython::Macros.Py_RETURN_NONE
87
+ end
63
88
 
64
- def self.rtopSymbol(rSymbol)
65
- Python.PyString_FromString rSymbol.to_s
66
- end
89
+ # Convert a Ruby Symbol to a \Python String. Returns an FFI::Pointer to a
90
+ # PyStringObject.
91
+ def self.rtopSymbol(rSymbol)
92
+ RubyPython::Python.PyString_FromString rSymbol.to_s
93
+ end
67
94
 
68
- def self.rtopProc(rObj)
69
- pyMethodDef = Python::PyMethodDef.new
70
- callback = Proc.new do |py_self, py_args|
71
- ret = rObj.call(*RubyPyProxy.new(py_args).to_a)
72
- PyObject.convert(ret)[0].pointer
73
- end
74
- pyMethodDef[:ml_name] = FFI::MemoryPointer.from_string "Proc::#{rObj.object_id}"
75
- pyMethodDef[:ml_meth] = callback
76
- pyMethodDef[:ml_flags] = Python::METH_VARARGS
77
- pyMethodDef[:ml_doc] = nil
95
+ # Convert a Ruby Proc to a \Python Function. Returns an FFI::Pointer to a
96
+ # PyCFunction.
97
+ def self.rtopFunction(rObj)
98
+ proc = FFI::Function.new(:pointer, [:pointer, :pointer]) do |p_self, p_args|
99
+ retval = rObj.call(*ptorTuple(p_args))
100
+ pObject = retval.is_a?(RubyPython::RubyPyProxy) ? retval.pObject : RubyPython::PyObject.new(retval)
78
101
 
79
- Python::PyCFunction_New pyMethodDef, nil
102
+ # make sure the refcount is >1 when pObject is destroyed
103
+ pObject.xIncref
104
+ pObject.pointer
80
105
  end
81
106
 
82
- #If possible converts a ruby type to an equivalent
83
- #python native type.
84
- #@param rObj a native ruby type
85
- #@param [Boolean] is_key whether this object will be used as a key in a
86
- # python dict.
87
- #@return [FFI::Pointer] a pointer to a C PyObject\*
88
- #@raise [UnsupportedConversion]
89
- def self.rtopObject(rObj, is_key=false)
90
- case rObj
91
- when String
92
- rtopString rObj
93
- when Array
94
- # If this object is going to be used as a
95
- # hash key we should make it a tuple instead
96
- # of a list
97
- if is_key
98
- rtopArrayToTuple rObj
99
- else
100
- rtopArrayToList rObj
101
- end
102
- when Hash
103
- rtopHash rObj
104
- when Fixnum
105
- rtopFixnum rObj
106
- when Bignum
107
- rtopBignum rObj
108
- when Float
109
- rtopFloat rObj
110
- when true
111
- rtopTrue
112
- when false
113
- rtopFalse
114
- when Symbol
115
- rtopSymbol rObj
116
- when nil
117
- rtopNone
118
- when Proc, Method
119
- raise UnsupportedConversion.new("Python to Ruby callbacks not suppported in legacy mode") if RubyPython.legacy_mode
120
- rtopProc rObj
107
+ defn = RubyPython::Python::PyMethodDef.new
108
+ defn[:ml_name] = FFI::MemoryPointer.from_string("RubyPython::Proc::%s" % rObj.object_id)
109
+ defn[:ml_meth] = proc
110
+ defn[:ml_flags] = RubyPython::Python::METH_VARARGS
111
+ defn[:ml_doc] = nil
112
+
113
+ return RubyPython::Python.PyCFunction_New(defn, nil)
114
+ end
115
+
116
+ # This will attempt to convert a Ruby object to an equivalent \Python
117
+ # native type. Returns an FFI::Pointer to a \Python object (the
118
+ # appropriate Py…Object C structure). If the conversion is unsuccessful,
119
+ # will raise UnsupportedConversion.
120
+ #
121
+ # [rObj] A native Ruby object.
122
+ # [is_key] Set to +true+ if the provided Ruby object will be used as a
123
+ # key in a \Python +dict+. (This primarily matters for Array
124
+ # conversion.)
125
+ def self.rtopObject(rObj, is_key = false)
126
+ case rObj
127
+ when String
128
+ rtopString rObj
129
+ when Array
130
+ # If this object is going to be used as a hash key we should make it a
131
+ # tuple instead of a list
132
+ if is_key
133
+ rtopArrayToTuple rObj
121
134
  else
122
- raise UnsupportedConversion.new("Unsupported type for RTOP conversion." )
135
+ rtopArrayToList rObj
123
136
  end
137
+ when Hash
138
+ rtopHash rObj
139
+ when Fixnum
140
+ rtopFixnum rObj
141
+ when Bignum
142
+ rtopBignum rObj
143
+ when Float
144
+ rtopFloat rObj
145
+ when true
146
+ rtopTrue
147
+ when false
148
+ rtopFalse
149
+ when Symbol
150
+ rtopSymbol rObj
151
+ when Proc, Method
152
+ if RubyPython.legacy_mode
153
+ raise UnsupportedConversion.new("Callbacks are not supported in Legacy Mode.")
154
+ end
155
+ rtopFunction rObj
156
+ when Method
157
+ rtopFunction rObj
158
+ when nil
159
+ rtopNone
160
+ when RubyPython::PyObject
161
+ rObj.pointer
162
+ else
163
+ raise UnsupportedConversion.new("Unsupported type #{rObj.class} for conversion.")
124
164
  end
165
+ end
125
166
 
126
- def self.ptorString(pString)
127
- Python.PyString_AsString(pString)
128
- end
167
+ # Convert an FFI::Pointer to a \Python String (PyStringObject) to a Ruby
168
+ # String.
169
+ def self.ptorString(pString)
170
+ strPtr = FFI::MemoryPointer.new(:pointer)
171
+ sizePtr = FFI::MemoryPointer.new(:ssize_t)
129
172
 
130
- def self.ptorList(pList)
131
- rb_array = []
132
- list_size = Python.PyList_Size(pList)
173
+ RubyPython::Python.PyString_AsStringAndSize(pString, strPtr, sizePtr)
133
174
 
134
- list_size.times do |i|
135
- element = Python.PyList_GetItem(pList, i)
136
- Python.Py_IncRef element
137
- rObject = ptorObject(element)
138
- rb_array.push rObject
139
- end
175
+ size = case FFI.find_type(:ssize_t)
176
+ when FFI.find_type(:long)
177
+ sizePtr.read_long
178
+ when FFI.find_type(:int)
179
+ sizePtr.read_int
180
+ when FFI.find_type(:long_long)
181
+ sizePtr.read_long_long
182
+ else
183
+ nil
184
+ end
140
185
 
141
- rb_array
142
- end
186
+ strPtr.read_pointer.read_string(size)
187
+ end
143
188
 
144
- def self.ptorInt(pNum)
145
- Python.PyInt_AsLong pNum
146
- end
189
+ # Convert an FFI::Pointer to a \Python List (PyListObject) to a Ruby
190
+ # Array.
191
+ def self.ptorList(pList)
192
+ rb_array = []
193
+ list_size = RubyPython::Python.PyList_Size(pList)
147
194
 
148
- def self.ptorLong(pNum)
149
- Python.PyLong_AsLong(pNum)
150
- #TODO Overflow Checking
195
+ list_size.times do |i|
196
+ element = RubyPython::Python.PyList_GetItem(pList, i)
197
+ # PyList_GetItem returns borrowed ref
198
+ RubyPython::Python.Py_IncRef element
199
+ rObject = ptorObject(element)
200
+ rb_array.push rObject
151
201
  end
152
202
 
153
- def self.ptorFloat(pNum)
154
- Python.PyFloat_AsDouble(pNum)
155
- end
203
+ rb_array
204
+ end
156
205
 
157
- def self.ptorTuple(pTuple)
158
- pList = Python.PySequence_List pTuple
159
- rArray = ptorList pList
160
- Python.Py_DecRef pList
161
- rArray
162
- end
206
+ # Convert an FFI::Pointer to a \Python Int (PyIntObject) to a Ruby Fixnum.
207
+ def self.ptorInt(pNum)
208
+ RubyPython::Python.PyInt_AsLong(pNum)
209
+ end
163
210
 
164
- def self.ptorDict(pDict)
165
- rb_hash = {}
211
+ # Convert an FFI::Pointer to a \Python Long (PyLongObject) to a Ruby
212
+ # Fixnum. This version does not do overflow checking, but probably should.
213
+ def self.ptorLong(pNum)
214
+ RubyPython::Python.PyLong_AsLong(pNum)
215
+ # TODO Overflow Checking
216
+ end
166
217
 
167
- pos = FFI::MemoryPointer.new :ssize_t
168
- pos.write_int 0
169
- key = FFI::MemoryPointer.new :pointer
170
- val = FFI::MemoryPointer.new :pointer
218
+ # Convert an FFI::Pointer to a \Python Float (PyFloatObject) to a Ruby
219
+ # Float.
220
+ def self.ptorFloat(pNum)
221
+ RubyPython::Python.PyFloat_AsDouble(pNum)
222
+ end
171
223
 
172
- while Python.PyDict_Next(pDict, pos, key, val) != 0
173
- pKey = key.read_pointer
174
- pVal = val.read_pointer
175
- rKey = ptorObject(pKey)
176
- rVal = ptorObject(pVal)
177
- rb_hash[rKey] = rVal
178
- end
224
+ # Convert an FFI::Pointer to a \Python Tuple (PyTupleObject) to a Ruby
225
+ # Array.
226
+ def self.ptorTuple(pTuple)
227
+ pList = RubyPython::Python.PySequence_List pTuple
228
+ rArray = ptorList pList
229
+ RubyPython::Python.Py_DecRef pList
230
+ rArray
231
+ end
179
232
 
180
- rb_hash
233
+ # Convert an FFI::Pointer to a \Python Dictionary (PyDictObject) to a Ruby
234
+ # Hash.
235
+ def self.ptorDict(pDict)
236
+ rb_hash = {}
237
+
238
+ pos = FFI::MemoryPointer.new :ssize_t
239
+ pos.write_int 0
240
+ key = FFI::MemoryPointer.new :pointer
241
+ val = FFI::MemoryPointer.new :pointer
242
+
243
+ while RubyPython::Python.PyDict_Next(pDict, pos, key, val) != 0
244
+ pKey = key.read_pointer
245
+ pVal = val.read_pointer
246
+ rKey = ptorObject(pKey)
247
+ rVal = ptorObject(pVal)
248
+ rb_hash[rKey] = rVal
181
249
  end
182
250
 
251
+ rb_hash
252
+ end
183
253
 
184
- #Converts a pointer to a Python object into a native ruby type, if
185
- #possible. Otherwise raises an error.
186
- #@param [FFI::Pointer] pObj a pointer to a Python object
187
- #@return a native ruby object.
188
- #@raise {UnsupportedConversion}
189
- def self.ptorObject(pObj)
190
- if Macros.PyObject_TypeCheck(pObj, Python.PyString_Type.to_ptr) != 0
191
- ptorString pObj
192
- elsif Macros.PyObject_TypeCheck(pObj, Python.PyList_Type.to_ptr) != 0
193
- ptorList pObj
194
- elsif Macros.PyObject_TypeCheck(pObj, Python.PyInt_Type.to_ptr) != 0
195
- ptorInt pObj
196
- elsif Macros.PyObject_TypeCheck(pObj, Python.PyLong_Type.to_ptr) != 0
197
- ptorLong pObj
198
- elsif Macros.PyObject_TypeCheck(pObj, Python.PyFloat_Type.to_ptr) != 0
199
- ptorFloat pObj
200
- elsif Macros.PyObject_TypeCheck(pObj, Python.PyTuple_Type.to_ptr) != 0
201
- ptorTuple pObj
202
- elsif Macros.PyObject_TypeCheck(pObj, Python.PyDict_Type.to_ptr) != 0
203
- ptorDict pObj
204
- elsif pObj == Macros.Py_True
205
- true
206
- elsif pObj == Macros.Py_False
207
- false
208
- elsif pObj == Macros.Py_None
209
- nil
210
- else
211
- pObj
212
- end
254
+ # Converts a pointer to a \Python object into a native Ruby type, if
255
+ # possible. If the conversion cannot be done, the Python object will be
256
+ # returned unmodified.
257
+ #
258
+ # [pObj] An FFI::Pointer to a \Python object.
259
+ def self.ptorObject(pObj)
260
+ if RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyString_Type.to_ptr) != 0
261
+ ptorString pObj
262
+ elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyList_Type.to_ptr) != 0
263
+ ptorList pObj
264
+ elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyInt_Type.to_ptr) != 0
265
+ ptorInt pObj
266
+ elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyLong_Type.to_ptr) != 0
267
+ ptorLong pObj
268
+ elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyFloat_Type.to_ptr) != 0
269
+ ptorFloat pObj
270
+ elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyTuple_Type.to_ptr) != 0
271
+ ptorTuple pObj
272
+ elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyDict_Type.to_ptr) != 0
273
+ ptorDict pObj
274
+ elsif pObj == RubyPython::Macros.Py_True
275
+ true
276
+ elsif pObj == RubyPython::Macros.Py_False
277
+ false
278
+ elsif pObj == RubyPython::Macros.Py_None
279
+ nil
280
+ else
281
+ pObj
213
282
  end
214
283
  end
215
284
  end