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.

Comments (0)

Files changed (8)

+=== 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.,


 #   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


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


+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

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"]


       ["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


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


   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,
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.