Austin Ziegler  committed e853ed9

Adding RubyPython::Tuple.

To simplify cases where actual Python tuples are required, adding a
class (a subclass of ::Array) to allow for explicit conversion to tuple

The semantics are best handled with the class method
RubyPython::Tuple.tuple(), which makes a #dup of the Array parameter.
This matches the result of the tuple() method in Python.

  • Participants
  • Parent commits ac347b2
  • Branches default

Comments (0)

Files changed (8)

File History.rdoc

+=== 0.6.0 / 2011-MM-DD
+* Minor Enhancements:
+  * Added RubyPython::Tuple, a simple subclass of ::Array that will correctly
+    be converted to a Python tuple object such that isinstance(x, tuple)
+    returns True.
 === 0.5.3 / 2011-10-22
 * Bug Fixes:
   * Improved 64-bit Python detection on Unixes with Linux-like semantics (e.g.,

File lib/rubypython.rb

 #   puts cPickle.dumps("RubyPython is awesome!").rubify
 #   RubyPython.stop
 module RubyPython
-  VERSION = '0.5.3' #:nodoc:
+  VERSION = '0.6.0' #:nodoc:
   # Do not load the FFI interface by default. Wait until the user asks for
   # it.
 require 'rubypython/rubypyproxy'
 require 'rubypython/pymainclass'
 require 'rubypython/pygenerator'
+require 'rubypython/tuple'
 module RubyPython
   class << self

File lib/rubypython/conversion.rb

-  # Convert a Ruby Array to \Python Tuple. Returns an FFI::Pointer to a
-  # PyTupleObject.
+  # Convert a Ruby Array (including the subclass RubyPython::Tuple) to
+  # \Python \tuple. Returns an FFI::Pointer to a PyTupleObject.
   def self.rtopArrayToTuple(rArray)
     pList = rtopArrayToList(rArray)
     pTuple = RubyPython::Python.PySequence_Tuple(pList)
     case rObj
     when String
       rtopString rObj
+    when RubyPython::Tuple
+      rtopArrayToTuple 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
-  # Convert an FFI::Pointer to a \Python Tuple (PyTupleObject) to a Ruby
-  # Array.
+  # Convert an FFI::Pointer to a \Python Tuple (PyTupleObject) to an
+  # instance of RubyPython::Tuple, a subclass of the Ruby Array class.
   def self.ptorTuple(pTuple)
     pList = RubyPython::Python.PySequence_List pTuple
     rArray = ptorList pList
     RubyPython::Python.Py_DecRef pList
-    rArray
+    RubyPython::Tuple.tuple(rArray)
   # Convert an FFI::Pointer to a \Python Dictionary (PyDictObject) to a Ruby

File lib/rubypython/tuple.rb

+module RubyPython
+  # A subclass of ::Array that will convert to a Python Tuple automatically.
+  class Tuple < ::Array
+    def self.tuple(array)
+      value =
+      value.replace(array.dup)
+      value
+    end
+  end

File rubypython.gemspec do |s| = "rubypython"
-  s.version = "0.5.2"
+  s.version = "0.6.0"
   s.required_rubygems_version =">= 0") if s.respond_to? :required_rubygems_version=
   s.authors = ["Steeve Morin", "Austin Ziegler", "Zach Raines"]

File spec/conversion_spec.rb

       ["a string", "a string", AString],
       ["a string_with_nulls", "a string_with_nulls", AStringWithNULLs],
       ["a list", "an array", AnArray],
-      ["a tuple", "an array", AnArray],
+      ["a tuple", "a tuple", ATuple],
       ["a dict", "a hash", AConvertedHash],
       ["python True", "true", true],
       ["python False", "false", false],
     ].each do |py_type, rb_type, output|
       it "should convert #{py_type} to #{rb_type}" do
         py_object_ptr = @objects.__send__(py_type.sub(' ', '_')).pObject.pointer
-        subject.ptorObject(py_object_ptr).should == output
+        obj = subject.ptorObject(py_object_ptr)
+        obj.should == output
+        obj.class.should == output.class
       ["a string_with_nulls", "a string_with_nulls", AStringWithNULLs],
       ["a string", "a symbol", ASym],
       ["a list", "an array", AnArray],
+      ["a tuple", "a tuple", ATuple ],
       ["a dict", "a hash", AConvertedHash],
       ["python True", "true", true],
       ["python False", "false", false],
     it "should raise an exception when it cannot convert" do
       lambda { subject.rtopObject(Class) }.should raise_exception(subject::UnsupportedConversion)
+    it "should convert a tuple correctly" do
+      @basics.expects_tuple(AnArray).should == false
+      @basics.expects_tuple(RubyPython::Tuple.tuple(AnArray)).should == true
+    end

File spec/python_helpers/

 def named_args(arg1, arg2):
   return [arg1, arg2]
+def expects_tuple(tvalue):
+  return isinstance(tvalue, tuple)

File spec/spec_helper.rb

   AChar = 'a'
   AFloat = 1.0
   AnArray = [AnInt, AChar, AFloat, AString]
+  ATuple = RubyPython::Tuple.tuple(AnArray)
+# ATuple << AnInt << AChar << AFloat << AString
   ASym = :sym
   AHash = {
     AnInt => AnInt,