lib/rubypython.rb in rubypython-0.2.11 vs lib/rubypython.rb in rubypython-0.3.0

- old
+ new

@@ -1,126 +1,130 @@ -require 'rubypython_bridge' -require 'rubypython/wrapper_extensions' +require 'rubypython/core_ext/string' +require 'rubypython/python' +require 'rubypython/pythonerror' +require 'rubypython/pyobject' +require 'rubypython/rubypyproxy' +require 'rubypython/pymainclass' -=begin rdoc -This module provides the direct user interface for the RubyPython extension. +#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 -The majority of the functionality lies in the _RubyPythonBridge_ module, which is provided -by the C extension. However, the end user should only worry about dealing with the RubyPython -module as that is designed for user interaction. Furthermore the RubyPythonBridge is somewhat -bad with memory management and using it directly may result in some strange crashes. + class << self -==Usage -It is important to remember that the Python Interpreter must be started before the bridge -is functional. This may be done by two methods. One is to use the +start+ function. -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!" - RubyPython.stop - -The other method is preferable if one wants a simpler approach. This other method is to use -<tt>RubyPython.run</tt>. run takes a block which is evaluated in the scope of the RubyPython module. -In addition, the interpreter is started before the block is run and halted at its completion. -This allows one to do something like the following: - RubyPython.run do - cPickle=import "cPickle" - puts cPickle.dumps "RubyPython is still awesome!" - end + #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 + # + #@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 + attr_accessor :legacy_mode -The downside to the above method is that the block has no access to the encompassing scope. An -alternative is to use <tt>RubyPython.session</tt>. The downside to this approach is that the module -methods are not available by their unqualified names: i.e. - irb(main):001:0> RubyPython.session do - irb(main):002:1* cPickle=import "cPickle" - irb(main):003:1> end - NoMethodError: undefined method `import' for main:Object - from (irb):2 - from ./rubypython.rb:93:in `call' - from ./rubypython.rb:93:in `session' - from (irb):1 - -However: - irb(main):001:0> RubyPython.session do - irb(main):002:1* cPickle=RubyPython.import "cPickle" - irb(main):003:1> puts cPickle.dumps "RubyPython is still awesome!" - irb(main):004:1> end - S'RubyPython is still awesome!' - . - => nil + #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. + #@return [Boolean] returns true if the interpreter was started here + # and false otherwise + def start + if Python.Py_IsInitialized != 0 + return false + end + Python.Py_Initialize + true + end -A compromise can be achieved by just including the RubyPython module into the scope you're -working in. + #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 + def stop + if Python.Py_IsInitialized !=0 + PyMain.main = nil + PyMain.builtin = nil + RubyPython::Operators.send :class_variable_set, '@@operator', nil + Python.Py_Finalize + RubyPython::PyObject::AutoPyPointer.current_pointers.clear + return true + end + false + end -If you really wish to be free of dealing with the interpreter, just import 'rubypython/session'. -This will start the interpreter on import and will halt it when execution ends. - -==Errors -The RubyPythonModule defines a new error object, PythonError. Should any error occur within -the Python interpreter, the class and value of the error will be passed back into ruby within -the text of the raised PythonError. - irb(main):001:0> RubyPython.start - => true - irb(main):002:0> RubyPython.import "does not exist" - PythonError: ImportError:(No module named does not exist) + #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 + def import(mod_name) + pModule = Python.PyImport_ImportModule mod_name + if(PythonError.error?) + raise PythonError.handle_error + end + pymod = PyObject.new pModule + RubyPyModule.new(pymod) + end - from ./rubypython.rb:66:in `initialize' - from ./rubypython.rb:66:in `import' - from ./rubypython.rb:66:in `import' - from (irb):2 -=end -module RubyPython - - # Used to started the python interpreter. Delegates to RubyPythonBridge - # - # RubyPython.start - # --Some python code-- - # RubyPython.stop - # - # Also see, _stop_ - def self.start() #=> true||false - RubyPythonBridge.start - 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 + stop + result + end - # Used to end the python session. Adds some cleanup on top of RubyPythonBridge.stop - def self.stop() #=> true,false - ObjectSpace.each_object(RubyPythonBridge::RubyPyObject) do |o| - o.free_pobj + #The same as {session} except that the block is executed within the scope + #of the RubyPython module. + def run(&block) + start + result = module_eval(&block) + stop end - PyMain.main=nil - PyMain.builtin=nil - RubyPythonBridge.stop end - - # Import the python module +mod+ and return it wrapped as a ruby object - def self.import(mod) - RubyPythonBridge.import(mod) - end - - # Handles the setup and cleanup involved with using the interpreter for you. - # Note that all Python object will be effectively scope to within the block - # as the embedded interpreter will be halted at its end. The supplied block is - # run within the scope of the RubyPython module. - # - # Alternatively the user may prefer RubyPython.session which simples handles - # initialization and cleanup of the interpreter. - def self.run(&block) - start - module_eval(&block) - stop - end - - # Simply starts the interpreter, runs the supplied block, and stops the interpreter. - def self.session(&block) - start - retval = block.call - stop - return retval - end end - -