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
Public Class Methods
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
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
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.
Delegates method calls to proxied Python objects.
Delegation Rules¶ ↑
-
If the method ends with a question-mark (e.g.,
nil?
), it can only be a Ruby method onRubyPyProxy
. Attempt to reveal it (RubyPyProxy
is aBlankObject
) and call it. -
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. -
If the method ends with an exclamation point (e.g.,
foo!
) we are attempting to call a method with keyword arguments. -
The
Python
method or value will be called, if it's callable. -
RubyPython
will wrap the return value in aRubyPyProxy
object -
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
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
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
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
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
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
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
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
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