lib/rubypython.rb in rubypython-0.3.2 vs lib/rubypython.rb in rubypython-0.5.0
- old
+ new
@@ -1,176 +1,270 @@
-require 'rubypython/core_ext/string'
+# 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.0' #: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'
-
-#This module provides the direct user interface for the RubyPython extension.
-#
-#RubyPython interfaces to the Python C API via the {Python} module using the
-#Ruby FFI gem. However, the end user should only worry about dealing with the
-#methods made avaiable via the RubyPython module.
-#
-#Usage
-#-----
-#It is important to remember that the Python Interpreter must be
-#started before the bridge is functional. This will start the embedded
-#interpreter. If this approach is used, the user should remember to call
-#RubyPython.stop when they are finished with Python.
-#@example
-# RubyPython.start
-# cPickle = RubyPython.import "cPickle"
-# puts cPickle.dumps("RubyPython is awesome!").rubify
-# RubyPython.stop
-#
-#Legacy Mode vs Normal Mode
-#---------------------------
-#By default RubyPython always returns a proxy class which refers method calls to
-#the wrapped Python object. If you instead would like RubyPython to aggressively
-#attempt conversion of return values, as it did in RubyPython 0.2.x, then you
-#should set {RubyPython.legacy_mode} to true. In this case RubyPython will
-#attempt to convert any return value from Python to a native Ruby type, and only
-#return a proxy if conversion is not possible. For further examples see
-#{RubyPython.legacy_mode}.
module RubyPython
-
-
class << self
-
- #Determines whether RubyPython is operating in Normal Mode or Legacy Mode.
- #If legacy_mode is true, RubyPython switches into a mode compatible with
- #versions < 0.3.0. All Python objects returned by method invocations are
- #automatically converted to natve Ruby Types if RubyPython knows how to do
- #this. Only if no such conversion is known are the objects wrapped in proxy
- #objects. Otherwise RubyPython automatically wraps all returned objects as
- #an instance of {RubyPyProxy} or one of its subclasses.
- #@return [Boolean]
- #@example Normal Mode
- # RubyPython.start
- # string = RubyPython.import 'string'
- # ascii_letters = string.ascii_letters # Here ascii_letters is a proxy object
- # puts ascii_letters.rubify # we use the rubify method to convert it to a
- # # native type
- # RubyPython.stop
+ # Controls whether RubyPython is operating in <em>Normal Mode</em> or
+ # <em>Legacy Mode</em>.
#
- #@example Legacy Mode
- # RubyPython.legacy_mode = true
- # RubyPython.start
- # string = RubyPython.import 'string'
- # ascii_letters = string.ascii_letters # Here ascii_letters is a native ruby string
- # puts ascii_letters # No explicit conversion is neccessary
- # RubyPython.stop
+ # === 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 ups the Python interpreter. This method **must** be run
- #before using any Python code. The only alternatives are use of the
- #{session} and {run} methods. If the Python interpreter needs to
- #be loaded or reloaded, it will be done here.
- #@return [Boolean] returns true if the interpreter was started here
- # and false otherwise
- def start
- unless @loaded
- @loaded = true
+ # 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
- if Python.Py_IsInitialized != 0
- return false
- end
+
+ return false if RubyPython::Python.Py_IsInitialized != 0
+
if @reload
reload_library
@reload = false
end
- Python.Py_Initialize
+
+ RubyPython::Python.Py_Initialize
notify :start
true
end
- #Stops the Python interpreter if it is running. Returns true if the
- #intepreter is stopped by this invocation. All wrapped Python objects
- #should be considered invalid after invocation of this method.
- #@return [Boolean] returns true if the interpreter was stopped here
- # and false otherwise
+ # 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 Python.Py_IsInitialized !=0
+ if defined? Python.Py_IsInitialized and Python.Py_IsInitialized != 0
Python.Py_Finalize
notify :stop
- return true
+ true
+ else
+ false
end
- false
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 object.
- #@param [String] mod_name the name of the module to import
- #@return [RubyPyModule] a proxy object wrapping the requested
- #module
+ # 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)
- pModule = Python.PyImport_ImportModule mod_name
- if(PythonError.error?)
- raise PythonError.handle_error
+ 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
- pymod = PyObject.new pModule
- RubyPyModule.new(pymod)
end
- #Execute the given block, starting the Python interperter before its execution
- #and stopping the interpreter after its execution. The last expression of the
- #block is returned; be careful that this is not a Python object as it will
- #become invalid when the interpreter is stopped.
- #@param [Block] block the code to be executed while the interpreter is running
- #@return the result of evaluating the given block
- def session
- start
- result = yield
+ # 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
- result
end
- #The same as {session} except that the block is executed within the scope
- #of the RubyPython module.
- #@return the result of evaluating the given block.
- def run(&block)
- start
- result = module_eval(&block)
+ # 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 => File.join(virtualenv, "bin", "python"))
+ activate
result
end
- private
+ # 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)
- if not @observers.nil?
- @observers.each do |o|
- o.update status
- end
+ @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'
+ load RubyPython::PYTHON_RB
true
end
-
+ private :reload_library
end
- [
- PyMain,
- Operators,
- PyObject::AutoPyPointer
- ].each do |observer|
- add_observer observer
- end
-
-
+ add_observer PyMain
+ add_observer Operators
+ add_observer PyObject::AutoPyPointer
end
-
-