Module: RubyPython

Defined in:
lib/rubypython.rb,
lib/rubypython.rb,
lib/rubypython/type.rb,
lib/rubypython/tuple.rb,
lib/rubypython/python.rb,
lib/rubypython/pygenerator.rb,
lib/rubypython/pymainclass.rb,
lib/rubypython/rubypyproxy.rb

Overview

RubyPython is a bridge between the Ruby and Python interpreters. It embeds a Python interpreter in the Ruby application's process using FFI and provides a means for wrapping, converting, and calling Python objects and methods.

Usage

The Python interpreter must be started before the RubyPython bridge is functional. The user can either manually manage the running of the interpreter as shown below, or use the RubyPython.run or RubyPython.session methods to automatically start and stop the interpreter.

RubyPython.start
cPickle = RubyPython.import "cPickle"
puts cPickle.dumps("RubyPython is awesome!").rubify
RubyPython.stop

Defined Under Namespace

Modules: Conversion, Macros, Operators, Python Classes: BlankObject, Interpreter, InvalidInterpreter, PyEnumerable, PyMainClass, PyObject, PythonError, RubyPyClass, RubyPyInstance, RubyPyModule, RubyPyProxy, Tuple

Constant Summary collapse

VERSION =
'0.6.4'
PyMain =

The accessible instance of PyMainClass.

RubyPython::PyMainClass.instance

Class Method Summary collapse

Class Method Details

.activate_virtualenvObject

Used to activate the virtualenv.


201
202
203
204
205
206
# File 'lib/rubypython.rb', line 201

def activate_virtualenv
  imp = import("imp")
  imp.load_source("activate_this",
                  File.join(File.dirname(RubyPython::Runtime.python),
                  "activate_this.py"))
end

.add_observer(object) ⇒ Object


209
210
211
212
213
# File 'lib/rubypython.rb', line 209

def add_observer(object)
  @observers ||= []
  @observers << object
  true
end

.generatorObject

Creates a Ruby lambda that acts like a Python generator. Uses RubyPython.generator_type and Fiber to work the generator as a coroutine.

Note: This method only exists in the RubyPython if the Fiber exists.


40
41
42
43
44
45
46
47
48
49
# File 'lib/rubypython/pygenerator.rb', line 40

def generator
  return lambda do |*args|
    fib = Fiber.new do
      yield *args
      Python.PyErr_SetNone(Python.PyExc_StopIteration)
      ::FFI::Pointer::NULL
    end
    generator_type.__call__(lambda { fib.resume })
  end
end

.generator_typeObject

Creates a Python generator object called rubypython_generator that accepts a callback and yields to it.

Note: This method only exists in the RubyPython if the Fiber exists.


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/rubypython/pygenerator.rb', line 17

def generator_type
  @generator_type ||= lambda do
    code = <<-EOM
def rubypython_generator(callback):
  while True:
    yield callback()
    EOM

    globals = PyObject.new({ "__builtins__" => PyMain.builtin.pObject, })
    empty_hash = PyObject.new({})
    ptr = Python.PyRun_String(code, Python::PY_FILE_INPUT, globals.pointer, empty_hash.pointer)
    ptr = Python.PyRun_String("rubypython_generator", Python::PY_EVAL_INPUT, globals.pointer, empty_hash.pointer)
    raise PythonError.handle_error if PythonError.error?
    RubyPyProxy.new(PyObject.new(ptr))
  end.call
end

.import(mod_name) ⇒ Object

Import a Python module into the interpreter and return a proxy object for it.

This is the preferred way to gain access to Python objects.

mod_name

The name of the module to import.


104
105
106
107
108
109
110
111
112
113
# File 'lib/rubypython.rb', line 104

def import(mod_name)
  if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
    pModule = Python.PyImport_ImportModule mod_name
    raise PythonError.handle_error if PythonError.error?
    pymod = PyObject.new pModule
    RubyPyModule.new(pymod)
  else
    raise "Python has not been started."
  end
end

.notify(status) ⇒ Object


216
217
218
219
220
221
222
# File 'lib/rubypython.rb', line 216

def notify(status)
  @observers ||= []
  @observers.each do |o|
    next if nil === o
    o.__send__ :python_interpreter_update, status
  end
end

.pythonObject

Returns an object describing the active Python interpreter. Returns nil if there is no active interpreter.


192
193
194
195
196
197
198
# File 'lib/rubypython.rb', line 192

def python
  if self.const_defined? :Runtime
    self::Runtime
  else
    nil
  end
end

.run(options = {}, &block) ⇒ Object

Starts the Python interpreter (optionally with options) and executes the provided block in the RubyPython module scope. When the block exits for any reason, the Python interpreter is stopped automatically.

The last executed expression of the block is returned. Be careful that the last expression of the block does not return a RubyPyProxy object, because the proxy object will be invalidated when the interpreter is stopped.

options

Configures the interpreter prior to starting it. Principally used to provide an alternative Python interpreter to start.

NOTE: In the current version of RubyPython, it is possible to change Python interpreters in a single Ruby process execution, but it is strongly discouraged as this may lead to segmentation faults. This feature is highly experimental and may be disabled in the future.

:call-seq: run(options = {}) { block to execute in RubyPython context }


161
162
163
164
165
166
# File 'lib/rubypython.rb', line 161

def run(options = {}, &block)
  start(options)
  self.module_eval(&block)
ensure
  stop
end

.session(options = {}) ⇒ Object

Starts the Python interpreter (optionally with options) and yields to the provided block. When the block exits for any reason, the Python interpreter is stopped automatically.

The last executed expression of the block is returned. Be careful that the last expression of the block does not return a RubyPyProxy object, because the proxy object will be invalidated when the interpreter is stopped.

options

Configures the interpreter prior to starting it. Principally used to provide an alternative Python interpreter to start.

NOTE: In the current version of RubyPython, it is possible to change Python interpreters in a single Ruby process execution, but it is strongly discouraged as this may lead to segmentation faults. This feature is highly experimental and may be disabled in the future.

:call-seq: session(options = {}) { block to execute }


134
135
136
137
138
139
# File 'lib/rubypython.rb', line 134

def session(options = {})
  start(options)
  yield
ensure
  stop
end

.start(options = {}) ⇒ Object

Starts the Python interpreter. One of RubyPython.start, RubyPython.session+, or RubyPython.run must be run before using any Python code. Returns true if the interpreter was started; false otherwise.

options

Configures the interpreter prior to starting it. Principally used to provide an alternative Python interpreter to start.

With no options provided:

RubyPython.start
sys = RubyPython.import 'sys'
p sys.version # => "2.6.6"
RubyPython.stop

With an alternative Python executable:

RubyPython.start(:python_exe => 'python2.7')
sys = RubyPython.import 'sys'
p sys.version # => "2.7.1"
RubyPython.stop

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/rubypython.rb', line 53

def start(options = {})
  RubyPython::Python.synchronize do
    # Has the Runtime interpreter been defined?
    if self.const_defined?(:Runtime)
      # If this constant is defined, then yes it is. Since it is, let's
      # see if we should print a warning to the user.
      unless Runtime == options
        warn "The Python interpreter has already been loaded from #{Runtime.python} and cannot be changed in this process. Continuing with the current runtime."
      end
    else
      interp = RubyPython::Interpreter.new(options)
      if interp.valid?
        self.const_set(:Runtime, interp)
      else
        raise RubyPython::InvalidInterpreter, "An invalid interpreter was specified."
      end
    end
    
    unless defined? RubyPython::Python.ffi_libraries
      Runtime.__send__(:infect!, RubyPython::Python)
    end

    return false if RubyPython::Python.Py_IsInitialized != 0
    RubyPython::Python.Py_Initialize
    notify :start
    true
  end
end

.start_from_virtualenv(virtualenv) ⇒ Object

Starts the Python interpreter for a virtualenv virtual environment. Returns true if the interpreter was started.

virtualenv

The root path to the virtualenv-installed Python interpreter.

RubyPython.start_from_virtualenv('/path/to/virtualenv')
sys = RubyPython.import 'sys'
p sys.version # => "2.7.1"
RubyPython.stop

NOTE: In the current version of RubyPython, it is possible to change Python interpreters in a single Ruby process execution, but it is strongly discouraged as this may lead to segmentation faults. This feature is highly experimental and may be disabled in the future.


184
185
186
187
188
# File 'lib/rubypython.rb', line 184

def start_from_virtualenv(virtualenv)
  result = start(:python_exe => File.join(virtualenv, "bin", "python"))
  activate_virtualenv
  result
end

.stopObject

Stops the Python interpreter if it is running. Returns true if the intepreter is stopped. All wrapped Python objects are invalid after invocation of this method. If you need the values within the Python proxy objects, be sure to call RubyPyProxy#rubify on them.


86
87
88
89
90
91
92
93
94
95
96
# File 'lib/rubypython.rb', line 86

def stop
  RubyPython::Python.synchronize do
    if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
      Python.Py_Finalize
      notify :stop
      true
    else
      false
    end
  end
end

.Type(name) ⇒ Object

Creates a Ruby class that inherits from a proxied Python object.


3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/rubypython/type.rb', line 3

def self.Type(name)
  mod, match, klass = name.rpartition(".")
  pymod = RubyPython.import(mod)
  pyclass = pymod.pObject.getAttr(klass)
  rclass = Class.new(RubyPyProxy) do
    define_method(:initialize) do |*args|
      args = PyObject.convert(*args)
      pTuple = PyObject.buildArgTuple(*args)
      pReturn = pyclass.callObject(pTuple)
      if PythonError.error?
        raise PythonError.handle_error
      end
      @pObject = pReturn
    end
  end
  return rclass
end

.yield(*args) ⇒ Object

Performs a Fiber.yield with the provided arguments, continuing the coroutine execution of the generator.

Note: This method only exists in the RubyPython if the Fiber exists.


56
57
58
# File 'lib/rubypython/pygenerator.rb', line 56

def yield(*args)
  Fiber.yield(*args)
end