class RubyPython::RubyPyProxy

In most cases, users will interact with RubyPyProxy objects that hold references to active objects in the Python interpreter. RubyPyProxy delegates method calls to Python objects, wrapping and returning the results as RubyPyProxy objects.

The allocation, deallocation, and reference counting on RubyPyProxy objects is automatic: RubyPython takes care of it all. When the object is garbage collected, the instance will automatically decrement its object reference count.

NOTE:

All RubyPyProxy objects become invalid when the Python interpreter is halted.

Calling Methods With Blocks

Any method which is forwarded to a Python object may be called with a block. The result of the method will passed as the argument to that block.

RubyPython.run do
  sys = RubyPython.import 'sys'
  sys.version { |v| v.rubify.split(' ') }
end
# => [ "2.6.1", … ]

Passing Procs and Methods to Python Methods

RubyPython supports passing Proc and Method objects to Python methods. The Proc or Method object must be passed explicitly. As seen above, supplying a block to a method will result in the return value of the method call being passed to the block.

When a Proc or Method is supplied as a callback, then arguments that it will be called with will be wrapped Python objects. It will therefore typically be necessary to write a wrapper around any Ruby callback that requires native Ruby objects.

# Python Code: sample.py
def apply_callback(callback, argument):
  return callback(argument)

# IRB Session
>> RubyPython.start
=> true
>> sys = RubyPython.import 'sys'
=> <module 'sys' (built-in)>
>> sys.path.append('.')
=> None
>> sample = RubyPython.import 'sample'
=> <module 'sample' from './sample.pyc'>
>> callback = Proc.new { |arg| arg * 2 }
=> # <Proc:0x000001018df490@(irb):5>
>> sample.apply_callback(callback, 21).rubify
=> 42
>> RubyPython.stop
=> true

Attributes

pObject[R]

Public Class Methods

new(pObject) click to toggle source

Creates a Python proxy for the provided Ruby object.

Only the following Ruby types can be represented in Python:

  • String

  • Array

  • Hash

  • Fixnum

  • Bignum

  • Float

  • Symbol (as a String)

  • Proc

  • Method

  • true (as True)

  • false (as False)

  • nil (as None)

# File lib/rubypython/rubypyproxy.rb, line 82
def initialize(pObject)
  if pObject.kind_of? PyObject
    @pObject = pObject
  else
    @pObject = PyObject.new pObject
  end
end

Public Instance Methods

inspect() click to toggle source

Returns the String representation of the wrapped object via a call to the object's __repr__ method, or the repr method in PyMain.

# File lib/rubypython/rubypyproxy.rb, line 216
def inspect
  self.__repr__.rubify
rescue PythonError, NoMethodError
  RubyPython::PyMain.repr(self).rubify
end
is_real_method?(mname)

The standard Ruby #respond_to? method has been renamed to allow RubyPython to query if the proxied Python object supports the method desired. Setter methods (e.g., foo=) are always supported.

Alias for: respond_to?
method_missing(name, *args, &block) click to toggle source

Delegates method calls to proxied Python objects.

Delegation Rules

  1. If the method ends with a question-mark (e.g., nil?), it can only be a Ruby method on RubyPyProxy. Attempt to reveal it (RubyPyProxy is a BlankObject) and call it.

  2. If the method ends with equals signs (e.g., value=) it's a setter and we can always set an attribute on a Python object.

  3. If the method ends with an exclamation point (e.g., foo!) we are attempting to call a method with keyword arguments.

  4. The Python method or value will be called, if it's callable.

  5. RubyPython will wrap the return value in a RubyPyProxy object

  6. If a block has been provided, the wrapped return value will be passed into the block.

# File lib/rubypython/rubypyproxy.rb, line 135
def method_missing(name, *args, &block)
  name = name.to_s

  if name =~ /\?$/
    begin
      RubyPyProxy.reveal(name.to_sym)
      return self.__send__(name.to_sym, *args, &block)
    rescue RuntimeError => exc
      raise NoMethodError.new(name) if exc.message =~ /Don't know how to reveal/
      raise
    end
  end

  kwargs = false

  if name =~ /=$/
    return @pObject.setAttr(name.chomp('='),
                            PyObject.new(args.pop))
  elsif name =~ /!$/
    kwargs = true
    name.chomp! "!"
  end

  raise NoMethodError.new(name) if !@pObject.hasAttr(name)

  pFunc = @pObject.getAttr(name)

  if pFunc.callable?
    if args.empty? and pFunc.class?
      pReturn = pFunc
    else
      if kwargs and args.last.is_a?(Hash)
        pKeywords = PyObject.new args.pop
      end
      pReturn = _method_call(pFunc, args, pKeywords)
      pFunc.xDecref
    end
  else
    pReturn = pFunc
  end

  result = _wrap(pReturn)

  if block
    block.call(result)
  else
    result
  end
end
methods() click to toggle source

Returns the methods on the Python object by calling the dir built-in.

# File lib/rubypython/rubypyproxy.rb, line 283
def methods
  pObject.dir.map { |x| x.to_sym }
end
respond_to?(mname) click to toggle source

RubyPython checks the attribute dictionary of the wrapped object to check whether it will respond to a method call. This should not return false positives but it may return false negatives. The built-in Ruby respond_to? method has been aliased to is_real_method?.

# File lib/rubypython/rubypyproxy.rb, line 114
def respond_to?(mname)
  return true if is_real_method?(mname)
  mname = mname.to_s
  return true if mname =~ /=$/
  @pObject.hasAttr(mname)
end
Also aliased as: is_real_method?
rubify() click to toggle source

RubyPython will attempt to translate the wrapped object into a native Ruby object. This will only succeed for simple built-in type.

# File lib/rubypython/rubypyproxy.rb, line 206
def rubify
  converted = @pObject.rubify
  if converted.kind_of? ::FFI::Pointer
    converted = self.class.new converted
  end
  converted
end
to_a() click to toggle source

Converts the wrapped Python object to a Ruby Array. Note that this only converts one level, so a nested array will remain a proxy object. Only wrapped objects which have an __iter__ method may be converted using to_a.

Note that for Python Dict objects, this method returns what you would get in Python, not in Ruby: a_dict.to_a returns an array of the dictionary's keys.

List to_a Returns an Array

>> RubyPython.start
=> true
>> list = RubyPython::RubyPyProxy.new([1, 'a', 2, 'b'])
=> [1, 'a', 2, 'b']
>> list.kind_of? RubyPython::RubyPyProxy
=> true
>> list.to_a
=> [1, 'a', 2, 'b']
>> RubyPython.stop
=> true

Dict to_a Returns An Array of Keys

>> RubyPython.start
=> true
>> dict = RubyPython::RubyPyProxy.new({1 => '2', :three => [4,5]})
=> {1: '2', 'three': [4, 5]}
>> dict.kind_of? RubyPython::RubyPyProxy
=> true
>> dict.to_a
=> [1, 'three']
>> RubyPython.stop
=> true

Non-Array Values Do Not Convert

>> RubyPython.start
=> true
>> item = RubyPython::RubyPyProxy.new(42)
=> 42
>> item.to_a
NoMethodError: __iter__
# File lib/rubypython/rubypyproxy.rb, line 270
def to_a
  iter = self.__iter__
  ary = []
  loop do
    ary << iter.next()
  end
rescue PythonError => exc
  raise if exc.message !~ /StopIteration/
  ary
end
to_enum() click to toggle source

Creates a PyEnumerable for this object. The object must have the __iter__ method.

# File lib/rubypython/rubypyproxy.rb, line 289
def to_enum
  PyEnumerable.new(@pObject)
end
to_s() click to toggle source

Returns the string representation of the wrapped object via a call to the object's __str__ method or the str method in PyMain.

# File lib/rubypython/rubypyproxy.rb, line 224
def to_s
  self.__str__.rubify
rescue PythonError, NoMethodError
  RubyPython::PyMain.str(self).rubify
end

Private Instance Methods

_method_call(pFunc, args, pKeywords) click to toggle source

Handles the of calling a wrapped callable Python object at a higher level than +PyObject#callObject+. For internal use only.

# File lib/rubypython/rubypyproxy.rb, line 187
def _method_call(pFunc, args, pKeywords)
  pTuple = PyObject.buildArgTuple(*args)
  pReturn = if pKeywords
    pFunc.callObjectKeywords(pTuple, pKeywords)
  else
    pFunc.callObject(pTuple)
  end

  # Clean up unused Python vars instead of waiting on Ruby's GC to
  # do it.
  pTuple.xDecref
  pKeywords.xDecref if pKeywords
  raise PythonError.handle_error if PythonError.error?
  pReturn
end
_wrap(pyobject) click to toggle source

Handles the job of wrapping up anything returned by a RubyPyProxy instance. Every returned # object is wrapped by an instance of RubyPyProxy

# File lib/rubypython/rubypyproxy.rb, line 92
def _wrap(pyobject)
  if pyobject.class?
    RubyPyClass.new(pyobject)
  else
    RubyPyProxy.new(pyobject)
  end
rescue Conversion::UnsupportedConversion => exc
  RubyPyProxy.new pyobject
end