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
@@ -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