# 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 module RubyPython VERSION = '0.5.2' #:nodoc: # Do not load the FFI interface by default. Wait until the user asks for # it. @load_ffi = false # Indicates whether the \Python DLL has been loaded. For internal use # only. def self.load_ffi? #:nodoc: @load_ffi end end require 'rubypython/blankobject' require 'rubypython/options' require 'rubypython/python' require 'rubypython/pythonerror' require 'rubypython/pyobject' require 'rubypython/rubypyproxy' require 'rubypython/pymainclass' require 'rubypython/pygenerator' module RubyPython class << self # 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 attr_accessor :legacy_mode # 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. 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 # 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. def stop if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0 Python.Py_Finalize notify :stop true else false end end # 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. 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 # 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 } def session(options = {}) start(options) yield ensure stop end # 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 } def run(options = {}, &block) start(options) module_eval(&block) ensure stop end # Starts the \Python interpreter for a # {virtualenv}[http://pypi.python.org/pypi/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. def start_from_virtualenv(virtualenv) result = start(:python_exe => File.join(virtualenv, "bin", "python")) activate result end # Returns an object describing the currently active Python interpreter. def python RubyPython::Python::EXEC end # Used to activate the virtualenv. def activate imp = import("imp") imp.load_source("activate_this", File.join(File.dirname(RubyPython::Python::EXEC.python), "activate_this.py")) end private :activate def add_observer(object) @observers ||= [] @observers << object true end private :add_observer def notify(status) @observers ||= [] @observers.each do |o| next if nil === o o.update status end end private :notify 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 private :reload_library end add_observer PyMain add_observer Operators add_observer PyObject::AutoPyPointer end