Python inside Ruby, the unholy alliance!
Ruby Python
Switch branches/tags
Latest commit 2b75cf6 Feb 19, 2011 Austin Ziegler Fixing RSpec1 format to RSpec2 in .rspec.

README.rdoc

Rupy: Python in your Ruby

Description

Rupy is a bridge between the Ruby and Python interpreters. It embeds a running Python interpreter in the application's process using FFI and provides a means for wrapping and converting Python objects. Rupy is based on Zach Raines's amazing RubyPython project.

Rupy uses FFI to marshall the data between the Ruby and Python VMs and make Python calls. Recently, Rupy has added the ability to:

  • Inherit from Python classes;

  • Configure callbacks from Python;

  • Run Python generators (on Ruby 1.9.2 or later).

Where

Rupy is currently managed from GitHub: [github.com/steeve/rupy].

Synopsis

Basic Usage

require "rupy"

Rupy.start # start the Python VM

cPickle = Rupy.import("cPickle")
p cPickle.dumps("Testing rupy").rubify

Rupy.stop # stop the Python VM

Specific Python Version

require "rupy"

Rupy.start(:python => "python2.7") # Can also be a full path

cPickle = Rupy.import("cPickle")
p cPickle.dumps("Testing rupy").rubify

Rupy.stop # stop the Python VM

VirtualEnv

# Easy
Rupy.start_from_virtualenv("/path/to/virtualenv")

# Or verbose
Rupy.start(:python => "/path/to/virtualenv/bin/python")
Rupy.activate

Iterator support

# Python
def readfile():
  for line in open("/some/file"):
    yield line

# Ruby
readfile.to_enum.each do |line|
  puts line
end

Python to Ruby callbacks

# Python
def dosomething(callback):
  print callback(5)

# Ruby
dosomething(lambda do |value|
  value * 2
end)

def mycallback(value)
  return value * 2
end

dosomething(method(:mycallback))

Python-style Generators

# Python
def test_generator(callback):
  for i in callback():
    print "Got %d" % i

# Ruby
test_generator(Rupy.generator do
  (0..10).each do |i|
    Rupy.yield i
  end
end)

Features / Problems

Features

  • Can handle simple conversion of Python builtin types to Ruby builtin types and vice versa

  • Can import Python modules

  • Can execute arbitrary methods on imported modules and return the result

  • Python objects can be treated as Ruby objects!

  • Python's standard library available to you from within Ruby.

  • Pass Ruby methods and procs as callbacks and call them from within Python code.

  • Specify the python executable to be loaded, including virtualenv.

Known Problems

  • Builtin Python methods which require a top level frame object (eval, dir, …) do not work properly at present.

  • There is no support for passing complicated (non-basic) Ruby types to Python.

Supported Versions

Python

Rupy is known to work with the C-based Python interpreter, versions 2.4 through 2.7; no work has yet been done to enable Python 3.0 support or to work with alternative Python implementations.

Ruby

Rupy is known to work with the C-based (MRI) Ruby interpreter on versions 1.8.7 and 1.9.2. However, it uses Ruby-FFI, so it may work with other implementations without modification.

What's planned

There are features that are not currently supported in Rupy that may be considered for future releases, dependent on need, interest, and solutions.

Simpler Imports

It might be nice to have some nice import helpers provided by Rupy to make the interface more seamless and provide advanced import features:

Import Aliasing

# Python
from mod2.mod1 import sym as mysym

# Ruby
py :from => "mod2.mod1", :import => "sym", :as => "mysym"
py :from => "mod2.mod1", :import => :sym, :as => :mysym
py :from => [ :mod2, :mod1 ], :import => :sym, :as => :mysym

# Python
import mod1 as mymod

# Ruby
py :import => "mod1", :as => "mymod"
py :import => :mod1, :as => :mymod

# Python
from mod2.mod1 import *

# Ruby
py :from => "mod2.mod1", :import => :*
pyrequire "mod2/mod1" # ruby style imports

Python named arguments

# Python
def foo(arg1, arg2):
  pass

# Ruby
foo(:arg2 => "bar2", :arg1 => "bar1")

# with Ruby 1.9
foo(arg2: "bar2", arg1: "bar1")

Catch Exceptions from Ruby

# Python
class MyFirstException(Exception):
  pass

class MySecondException(MyFirstException):
  pass

def test():
  raise MySecondException

# Ruby
begin
  test
rescue MyFirstException => e
  # We may need to work out name collisions
  puts e.message
end

Requirements

  • Python >= 2.4, < 3.0

  • Ruby >= 1.8.6

  • You must be able to build the ffi gem under your environment.

Note: RubyPython and Rupy have been tested on Mac OS 10.5.x

Install

gem install rupy

Why Rupy?

Steeve Morin: I'm in love with both languages and want to be able to take what's best from each. This is an idea that was in my head for quite some time, and seeing that Zach Raines only developed RubyPython sporadically, I decided to implement what was missing to address my use cases.

Austin Ziegler: I have a need to control some Python code through a Ruby application for a project I'm working on, so I found RubyPython.

I implemented and submitted a bug fix for Zach's implementation and he pointed me to Steeve's fork as something that would be developed more frequently. There's

History

This project owes a great debt to Zach Raines for RubyPython for the initial code. His original code (updated irregularly) can be found on [BitBucket](raineszm.bitbucket.org/rubypython/).

License

See License.rdoc for full details.

(The MIT License)

Copyright 2011 Steeve Morin, Austin Ziegler, and Zach Raines Portions copyright 2008–2011 Zach Raines

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.