Module: RubyPython

Defined in:
lib/rubypython.rb,
lib/rubypython.rb,
lib/rubypython/type.rb,
lib/rubypython/python.rb,
lib/rubypython/options.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, LegacyMode, Macros, Operators, Python Classes: BlankObject, PyEnumerable, PyMainClass, PyObject, PythonError, PythonExec, RubyPyClass, RubyPyInstance, RubyPyModule, RubyPyProxy

Constant Summary collapse

VERSION =

:nodoc:

'0.5.3'
PYTHON_RB =
__FILE__
NEED_RELOAD =

A list of options which require the Python library to be reloaded.

[ :python_exe ]
PyMain =

The accessible instance of PyMainClass.

RubyPython::PyMainClass.instance

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.legacy_modeObject

Controls whether RubyPython is operating in Normal Mode or Legacy Mode.

Normal Mode

By default, legacy_mode is false, meaning that any object returned from a Python function call will be wrapped in an instance of RubyPyProxy or one of its subclasses. This allows Python method calls to be forwarded to the Python object, even if it would otherwise be a native Ruby object.

RubyPython.session do
  string = RubyPython.import 'string'
  ascii_letters = string.ascii_letters
  puts ascii_letters.isalpha # => True
  puts ascii_letters.rubify.isalpha # throws NoMethodError
end

Legacy Mode

If legacy_mode is true, RubyPython automatically tries to convert returned objects to native Ruby object types. If there is no such conversion, the object remains wrapped in RubyPyProxy. This behaviour is the same as RubyPython 0.2 and earlier. This mode is not recommended and may be phased out for RubyPython 1.0.

RubyPython.legacy_mode = true
RubyPython.session do
  string = RubyPython.import 'string'
  ascii_letters = string.ascii_letters
  puts ascii_letters.isalpha # throws NoMethodError
end


72
73
74
# File 'lib/rubypython.rb', line 72

def legacy_mode
  @legacy_mode
end

Class Method Details

.activateObject

Used to activate the virtualenv.



231
232
233
234
235
236
# File 'lib/rubypython.rb', line 231

def activate
  imp = import("imp")
  imp.load_source("activate_this",
                  File.join(File.dirname(RubyPython::Python::EXEC.python),
                  "activate_this.py"))
end

.add_observer(object) ⇒ Object



239
240
241
242
243
# File 'lib/rubypython.rb', line 239

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

.clear_optionsvoid

This method returns an undefined value.

Reset the options hash.



61
62
63
64
# File 'lib/rubypython/options.rb', line 61

def clear_options
  @reload = @options.keys.any? { |k| NEED_RELOAD.include? k }
  @options.clear
end

.configure(options = {}) ⇒ Object

Allows one to set options for RubyPython’s execution. Parameters may be set either by supplying a hash argument or by supplying a block and calling setters on the provided OpenStruct. Returns a copy of the updated options hash.

options

A Hash of options to set.

The option currently supported is:

:python_exe

The name of or path to the Python executable for the

version of Python you wish to use.

RubyPython.run do
  RubyPython.import('sys').version.rubify.to_f # => 2.7
end

RubyPython.configure :python_exe => 'python2.6'
# => { :python_exe => "python2.6" }
RubyPython.run do
  RubyPython.import('sys').version.rubify.to_f # => 2.6
end

The options hash can also be passed directly to RubyPython.start, RubyPython.session, or RubyPython.run.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/rubypython/options.rb', line 37

def configure(options = {})
  old_values = Hash[*@options.select { |k, v| NEED_RELOAD.include? k }]

  if block_given?
    ostruct = OpenStruct.new @options
    yield ostruct
    olist = ostruct.instance_variable_get('@table').map { |k, v| [ k.to_sym, v ] }
    @options = Hash[*olist]
  end
  @options.merge!(options)

  @reload = true if NEED_RELOAD.any? { |k| @options[k] != old_values[k] } 
  options
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.



139
140
141
142
143
144
145
146
147
148
# File 'lib/rubypython.rb', line 139

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

.load_ffi?Boolean

Indicates whether the Python DLL has been loaded. For internal use only.

Returns:

  • (Boolean)


26
27
28
# File 'lib/rubypython.rb', line 26

def self.load_ffi? #:nodoc:
  @load_ffi
end

.notify(status) ⇒ Object



246
247
248
249
250
251
252
# File 'lib/rubypython.rb', line 246

def notify(status)
  @observers ||= []
  @observers.each do |o|
    next if nil === o
    o.update status
  end
end

.optionsObject

Returns a copy of the hash currently being used to determine run options. This allows the user to determine what options have been set. Modification of options should be done via the configure method.



55
56
57
# File 'lib/rubypython/options.rb', line 55

def options
  @options.dup
end

.pythonObject

Returns an object describing the currently active Python interpreter.



226
227
228
# File 'lib/rubypython.rb', line 226

def python
  RubyPython::Python::EXEC
end

.reload_libraryObject



255
256
257
258
259
260
261
262
263
# File 'lib/rubypython.rb', line 255

def reload_library
  # Invalidate the current Python instance, if defined.
  if defined? RubyPython::Python::EXEC and RubyPython::Python::EXEC
    RubyPython::Python::EXEC.instance_eval { invalidate! }
  end
  remove_const :Python
  load RubyPython::PYTHON_RB
  true
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 }



196
197
198
199
200
201
# File 'lib/rubypython.rb', line 196

def run(options = {}, &block)
  start(options)
  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 }



169
170
171
172
173
174
# File 'lib/rubypython.rb', line 169

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

.start(options = {}) ⇒ Object

Starts the Python interpreter. Either 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

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.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/rubypython.rb', line 98

def start(options = {})
  RubyPython.configure(options)

  unless @load_ffi
    @load_ffi = true
    @reload = false
    reload_library
  end

  return false if RubyPython::Python.Py_IsInitialized != 0

  if @reload
    reload_library
    @reload = false
  end

  RubyPython::Python.Py_Initialize
  notify :start
  true
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.



219
220
221
222
223
# File 'lib/rubypython.rb', line 219

def start_from_virtualenv(virtualenv)
  result = start(:python_exe => File.join(virtualenv, "bin", "python"))
  activate
  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.



123
124
125
126
127
128
129
130
131
# File 'lib/rubypython.rb', line 123

def stop
  if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
    Python.Py_Finalize
    notify :stop
    true
  else
    false
  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