Module: RubyPython::Conversion

Defined in:
lib/rubypython/conversion.rb

Overview

Acts as a namespace for methods to bidirectionally convert between native Ruby types and native Python types. Unsupported conversions raise UnsupportedConversion.

The methods in this module should be considered internal implementation to RubyPython as they all return FFI pointers to Python objects.

Defined Under Namespace

Classes: UnsupportedConversion

Class Method Summary collapse

Class Method Details

.ptorDict(pDict) ⇒ Object

Convert an FFI::Pointer to a Python Dictionary (PyDictObject) to a Ruby Hash.



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/rubypython/conversion.rb', line 235

def self.ptorDict(pDict)
  rb_hash = {}

  pos = FFI::MemoryPointer.new :ssize_t
  pos.write_int 0
  key = FFI::MemoryPointer.new :pointer
  val = FFI::MemoryPointer.new :pointer

  while RubyPython::Python.PyDict_Next(pDict, pos, key, val) != 0
    pKey = key.read_pointer
    pVal = val.read_pointer
    rKey = ptorObject(pKey)
    rVal = ptorObject(pVal)
    rb_hash[rKey] = rVal
  end

  rb_hash
end

.ptorFloat(pNum) ⇒ Object

Convert an FFI::Pointer to a Python Float (PyFloatObject) to a Ruby Float.



220
221
222
# File 'lib/rubypython/conversion.rb', line 220

def self.ptorFloat(pNum)
  RubyPython::Python.PyFloat_AsDouble(pNum)
end

.ptorInt(pNum) ⇒ Object

Convert an FFI::Pointer to a Python Int (PyIntObject) to a Ruby Fixnum.



207
208
209
# File 'lib/rubypython/conversion.rb', line 207

def self.ptorInt(pNum)
  RubyPython::Python.PyInt_AsLong(pNum)
end

.ptorList(pList) ⇒ Object

Convert an FFI::Pointer to a Python List (PyListObject) to a Ruby Array.



191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rubypython/conversion.rb', line 191

def self.ptorList(pList)
  rb_array = []
  list_size = RubyPython::Python.PyList_Size(pList)

  list_size.times do |i|
    element = RubyPython::Python.PyList_GetItem(pList, i)
    # PyList_GetItem returns borrowed ref
    RubyPython::Python.Py_IncRef element
    rObject = ptorObject(element)
    rb_array.push rObject
  end

  rb_array
end

.ptorLong(pNum) ⇒ Object

Convert an FFI::Pointer to a Python Long (PyLongObject) to a Ruby Fixnum. This version does not do overflow checking, but probably should.



213
214
215
216
# File 'lib/rubypython/conversion.rb', line 213

def self.ptorLong(pNum)
  RubyPython::Python.PyLong_AsLong(pNum)
  # TODO Overflow Checking
end

.ptorObject(pObj) ⇒ Object

Converts a pointer to a Python object into a native Ruby type, if possible. If the conversion cannot be done, the Python object will be returned unmodified.

pObj

An FFI::Pointer to a Python object.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/rubypython/conversion.rb', line 259

def self.ptorObject(pObj)
  if RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyString_Type.to_ptr) != 0
    ptorString pObj
  elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyList_Type.to_ptr) != 0
    ptorList pObj
  elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyInt_Type.to_ptr) != 0
    ptorInt pObj
  elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyLong_Type.to_ptr) != 0
    ptorLong pObj
  elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyFloat_Type.to_ptr) != 0
    ptorFloat pObj
  elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyTuple_Type.to_ptr) != 0
    ptorTuple pObj
  elsif RubyPython::Macros.PyObject_TypeCheck(pObj, RubyPython::Python.PyDict_Type.to_ptr) != 0
    ptorDict pObj
  elsif pObj == RubyPython::Macros.Py_True
    true
  elsif pObj == RubyPython::Macros.Py_False
    false
  elsif pObj == RubyPython::Macros.Py_None
    nil
  else
    pObj
  end
end

.ptorString(pString) ⇒ Object

Convert an FFI::Pointer to a Python String (PyStringObject) to a Ruby String.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/rubypython/conversion.rb', line 169

def self.ptorString(pString)
  strPtr  = FFI::MemoryPointer.new(:pointer)
  sizePtr = FFI::MemoryPointer.new(:ssize_t)

  RubyPython::Python.PyString_AsStringAndSize(pString, strPtr, sizePtr)

  size = case FFI.find_type(:ssize_t)
         when FFI.find_type(:long)
           sizePtr.read_long
         when FFI.find_type(:int)
           sizePtr.read_int
         when FFI.find_type(:long_long)
           sizePtr.read_long_long
         else
           nil
         end

  strPtr.read_pointer.read_string(size)
end

.ptorTuple(pTuple) ⇒ Object

Convert an FFI::Pointer to a Python Tuple (PyTupleObject) to a Ruby Array.



226
227
228
229
230
231
# File 'lib/rubypython/conversion.rb', line 226

def self.ptorTuple(pTuple)
  pList = RubyPython::Python.PySequence_List pTuple
  rArray = ptorList pList
  RubyPython::Python.Py_DecRef pList
  rArray
end

.rtopArrayToList(rArray) ⇒ Object

Convert a Ruby Array to Python List. Returns an FFI::Pointer to a PyListObject.



24
25
26
27
28
29
30
31
# File 'lib/rubypython/conversion.rb', line 24

def self.rtopArrayToList(rArray)
  size = rArray.length
  pList = RubyPython::Python.PyList_New size
  rArray.each_with_index do |el, i|
    RubyPython::Python.PyList_SetItem pList, i, rtopObject(el)
  end
  pList
end

.rtopArrayToTuple(rArray) ⇒ Object

Convert a Ruby Array to Python Tuple. Returns an FFI::Pointer to a PyTupleObject.



35
36
37
38
39
40
# File 'lib/rubypython/conversion.rb', line 35

def self.rtopArrayToTuple(rArray)
  pList = rtopArrayToList(rArray)
  pTuple = RubyPython::Python.PySequence_Tuple(pList)
  RubyPython::Python.Py_DecRef(pList)
  pTuple
end

.rtopBigNum(rNum) ⇒ Object

Convert a Ruby Bignum to a Python Long. Returns an FFI::Pointer to a PyLongObject.



61
62
63
# File 'lib/rubypython/conversion.rb', line 61

def self.rtopBigNum(rNum)
  RubyPython::Python.PyLong_FromLong(rNum)
end

.rtopFalseObject

Returns a Python False value (equivalent to Ruby’s false). Returns an FFI::Pointer to Py_ZeroStruct.



73
74
75
# File 'lib/rubypython/conversion.rb', line 73

def self.rtopFalse
  RubyPython::Macros.Py_RETURN_FALSE
end

.rtopFixnum(rNum) ⇒ Object

Convert a Ruby Fixnum to a Python Int. Returns an FFI::Pointer to a PyIntObject.



55
56
57
# File 'lib/rubypython/conversion.rb', line 55

def self.rtopFixnum(rNum)
  RubyPython::Python.PyInt_FromLong(rNum)
end

.rtopFloat(rNum) ⇒ Object

Convert a Ruby float to a Python Float. Returns an FFI::Pointer to a PyFloatObject.



67
68
69
# File 'lib/rubypython/conversion.rb', line 67

def self.rtopFloat(rNum)
  RubyPython::Python.PyFloat_FromDouble(rNum)
end

.rtopFunction(rObj) ⇒ Object

Convert a Ruby Proc to a Python Function. Returns an FFI::Pointer to a PyCFunction.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rubypython/conversion.rb', line 97

def self.rtopFunction(rObj)
  proc = FFI::Function.new(:pointer, [:pointer, :pointer]) do |p_self, p_args|
    retval = rObj.call(*ptorTuple(p_args))
    pObject = retval.is_a?(RubyPython::RubyPyProxy) ? retval.pObject : RubyPython::PyObject.new(retval)

    # make sure the refcount is >1 when pObject is destroyed
    pObject.xIncref
    pObject.pointer
  end

  defn = RubyPython::Python::PyMethodDef.new
  defn[:ml_name] = FFI::MemoryPointer.from_string("RubyPython::Proc::%s" % rObj.object_id)
  defn[:ml_meth] = proc
  defn[:ml_flags] = RubyPython::Python::METH_VARARGS
  defn[:ml_doc] = nil

  return RubyPython::Python.PyCFunction_New(defn, nil)
end

.rtopHash(rHash) ⇒ Object

Convert a Ruby Hash to a Python Dict. Returns an FFI::Pointer to a PyDictObject.



44
45
46
47
48
49
50
51
# File 'lib/rubypython/conversion.rb', line 44

def self.rtopHash(rHash)
  pDict = RubyPython::Python.PyDict_New
  rHash.each do |k,v|
    RubyPython::Python.PyDict_SetItem pDict, rtopObject(k, key = true),
      rtopObject(v)
  end
  pDict
end

.rtopNoneObject

Returns a Python None value (equivalent to Ruby’s nil). Returns an FFI::Pointer to Py_NoneStruct.



85
86
87
# File 'lib/rubypython/conversion.rb', line 85

def self.rtopNone
  RubyPython::Macros.Py_RETURN_NONE
end

.rtopObject(rObj, is_key = false) ⇒ Object

This will attempt to convert a Ruby object to an equivalent Python native type. Returns an FFI::Pointer to a Python object (the appropriate Py…Object C structure). If the conversion is unsuccessful, will raise UnsupportedConversion.

rObj

A native Ruby object.

is_key

Set to true if the provided Ruby object will be used as a key in a Python dict. (This primarily matters for Array conversion.)



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/rubypython/conversion.rb', line 125

def self.rtopObject(rObj, is_key = false)
  case rObj
  when String
    rtopString rObj
  when Array
    # If this object is going to be used as a hash key we should make it a
    # tuple instead of a list
    if is_key
      rtopArrayToTuple rObj
    else
      rtopArrayToList rObj
    end
  when Hash
    rtopHash rObj
  when Fixnum
    rtopFixnum rObj
  when Bignum
    rtopBignum rObj
  when Float
    rtopFloat rObj
  when true
    rtopTrue
  when false
    rtopFalse
  when Symbol
    rtopSymbol rObj
  when Proc, Method
    if RubyPython.legacy_mode
      raise UnsupportedConversion.new("Callbacks are not supported in Legacy Mode.")
    end
    rtopFunction rObj
  when Method
    rtopFunction rObj
  when nil
    rtopNone
  when RubyPython::PyObject
    rObj.pointer
  else
    raise UnsupportedConversion.new("Unsupported type #{rObj.class} for conversion.")
  end
end

.rtopString(rString) ⇒ Object

Convert a Ruby string to a Python string. Returns an FFI::Pointer to a PyStringObject.



17
18
19
20
# File 'lib/rubypython/conversion.rb', line 17

def self.rtopString(rString)
  size = rString.respond_to?(:bytesize) ? rString.bytesize : rString.size
  RubyPython::Python.PyString_FromStringAndSize(rString, size)
end

.rtopSymbol(rSymbol) ⇒ Object

Convert a Ruby Symbol to a Python String. Returns an FFI::Pointer to a PyStringObject.



91
92
93
# File 'lib/rubypython/conversion.rb', line 91

def self.rtopSymbol(rSymbol)
  RubyPython::Python.PyString_FromString rSymbol.to_s
end

.rtopTrueObject

Returns a Python True value (equivalent to Ruby’s true). Returns an FFI::Pointer to Py_TrueStruct.



79
80
81
# File 'lib/rubypython/conversion.rb', line 79

def self.rtopTrue
  RubyPython::Macros.Py_RETURN_TRUE
end