class RubyPython::Interpreter
An instance of this class represents information about a particular Python interpreter.
This class represents the current Python interpreter. A class that represents a Python executable.
End users may get the instance that represents the current running Python interpreter (from RubyPython.python
), but should not directly instantiate this class.
Attributes
The Python library.
The name of the Python executable that is used. This is the value of 'sys.executable' for the Python interpreter provided in :python_exe
or 'python' if it is not provided.
On Mac OS X Lion (10.7), this value is '/usr/bin/python' for 'python'.
The system prefix for the Python interpreter. This is the value of 'sys.prefix'.
The version of the Python interpreter. This is a decimalized version of 'sys.version_info' (such that Python 2.7.1 is reported as '2.7').
The basename of the Python interpreter with a version number. This is mostly an intermediate value used to find the shared Python library, but /usr/bin/python is often a link to /usr/bin/python2.7 so it may be of value. Note that this does not include the full path to the interpreter.
Public Class Methods
Create a new instance of an Interpreter
instance describing a particular Python executable and shared library.
Expects a hash that matches the configuration options provided to RubyPython.start
; currently only one value is recognized in that hash:
-
:python_exe
: Specifies the name of the python executable to run.
# File lib/rubypython/interpreter.rb, line 38 def initialize(options = {}) @python_exe = options[:python_exe] # Windows: 'C:\\Python27\python.exe' # Mac OS X: '/usr/bin/ # The default interpreter might be python3 on some systems rc, majorversion = runpy "import sys; print(sys.version_info[0])" if majorversion == "3" warn "The python interpreter is python 3, switching to python2" @python_exe = "python2" end rc, @python = runpy "import sys; print sys.executable" if rc.exitstatus.nonzero? raise RubyPython::InvalidInterpreter, "An invalid interpreter was specified." end rc, @version = runpy "import sys; print '%d.%d' % sys.version_info[:2]" rc, @sys_prefix = runpy "import sys; print sys.prefix" if ::FFI::Platform.windows? flat_version = @version.tr('.', '') basename = File.basename(@python, '.exe') if basename =~ /(?:#{@version}|#{flat_version})$/ @version_name = basename else @version_name = "#{basename}#{flat_version}" end else basename = File.basename(@python) if basename =~ /#{@version}/ @version_name = basename elsif basename.end_with?("2") @version_name = "#{basename[0..-2]}#{@version}" else @version_name = "#{basename}#{@version}" end end @library = find_python_lib end
Public Instance Methods
Compare the current Interpreter
to the provided Interpreter
or configuration hash. A configuration hash will be converted to an Interpreter
object before being compared. :python_exe basename is used. If comparing against another Interpreter
object, the Interpreter
basename and version are used.
# File lib/rubypython/interpreter.rb, line 23 def ==(other) other = self.class.new(other) if other.kind_of? Hash return false unless other.kind_of? self.class (self.version == other.version) && (self.version_name == other.version_name) end
# File lib/rubypython/interpreter.rb, line 222 def debug_s(format = nil) system = "" system << "windows " if ::FFI::Platform.windows? system << "mac " if ::FFI::Platform.mac? system << "unix " if ::FFI::Platform.unix? system << "unknown " if system.empty? case format when :report s = <<-EOS python_exe: #{@python_exe} python: #{@python} version: #{@version} sys_prefix: #{@sys_prefix} version_name: #{@version_name} platform: #{system.chomp} library: #{@library.inspect} libbase: #{@libbase} libext: #{@libext} libname: #{@libname} locations: #{@locations.inspect} EOS else s = "#<#{self.class}: " s << "python_exe=#{@python_exe.inspect} " s << "python=#{@python.inspect} " s << "version=#{@version.inspect} " s << "sys_prefix=#{@sys_prefix.inspect} " s << "version_name=#{@version_name.inspect} " s << system s << "library=#{@library.inspect} " s << "libbase=#{@libbase.inspect} " s << "libext=#{@libext.inspect} " s << "libname=#{@libname.inspect} " s << "locations=#{@locations.inspect}" end s end
# File lib/rubypython/interpreter.rb, line 212 def inspect(debug = false) if debug debug_s elsif @python "#<#{self.class}: #{python} v#{version} #{sys_prefix} #{version_name}>" else "#<#{self.class}: invalid interpreter>" end end
# File lib/rubypython/interpreter.rb, line 163 def valid? if @python.nil? or @python.empty? false elsif @library.nil? or @library.empty? false else true end end
Private Instance Methods
# File lib/rubypython/interpreter.rb, line 80 def find_python_lib # By default, the library name will be something like # libpython2.6.so, but that won't always work. @libbase = "#{::FFI::Platform::LIBPREFIX}#{@version_name}" @libext = ::FFI::Platform::LIBSUFFIX @libname = "#{@libbase}.#{@libext}" # We may need to look in multiple locations for Python, so let's # build this as an array. @locations = [ File.join(@sys_prefix, "lib", @libname) ] if ::FFI::Platform.mac? # On the Mac, let's add a special case that has even a different # @libname. This may not be fully useful on future versions of OS # X, but it should work on 10.5 and 10.6. Even if it doesn't, the # next step will (/usr/lib/libpython<version>.dylib is a symlink # to the correct location). @locations << File.join(@sys_prefix, "Python") # Let's also look in the location that was originally set in this # library: File.join(@sys_prefix, "lib", "#{@realname}", "config", @libname) end if ::FFI::Platform.unix? # On Unixes, let's look in some standard alternative places, too. # Just in case. Some Unixes don't include a .so symlink when they # should, so let's look for the base cases of .so.1 and .so.1.0, too. [ @libname, "#{@libname}.1", "#{@libname}.1.0" ].each do |name| if ::FFI::Platform::ARCH != 'i386' @locations << File.join("/opt/local/lib64", name) @locations << File.join("/opt/lib64", name) @locations << File.join("/usr/local/lib64", name) @locations << File.join("/usr/lib64", name) @locations << File.join("/usr/lib/x86_64-linux-gnu", name) end @locations << File.join("/opt/local/lib", name) @locations << File.join("/opt/lib", name) @locations << File.join("/usr/local/lib", name) @locations << File.join("/usr/lib", name) end end if ::FFI::Platform.windows? # On Windows, the appropriate DLL is usually be found in # %SYSTEMROOT%\system or %SYSTEMROOT%\system32; as a fallback we'll # use C:\Windows\system{,32} as well as the install directory and the # install directory + libs. system_root = File.expand_path(ENV['SYSTEMROOT']).gsub(/\\/, '/') @locations << File.join(system_root, 'system', @libname) @locations << File.join(system_root, 'system32', @libname) @locations << File.join("C:/WINDOWS", "System", @libname) @locations << File.join("C:/WINDOWS", "System32", @libname) @locations << File.join(sys_prefix, @libname) @locations << File.join(sys_prefix, 'libs', @libname) @locations << File.join(system_root, "SysWOW64", @libname) @locations << File.join("C:/WINDOWS", "SysWOW64", @libname) end # Let's add alternative extensions; again, just in case. @locations.dup.each do |location| path = File.dirname(location) base = File.basename(location, ".#{@libext}") @locations << File.join(path, "#{base}.so") # Standard Unix @locations << File.join(path, "#{base}.dylib") # Mac OS X @locations << File.join(path, "#{base}.dll") # Windows end # Remove redundant locations @locations.uniq! library = nil @locations.each do |location| if File.exists? location library = location break end end library end
Infects the provided module with the Python FFI. Once a single module has been infected, the infect!
method is removed from RubyPython::Interpreter
.
# File lib/rubypython/python.rb, line 20 def infect!(mod) self.class.class_eval do undef :infect! end mod.extend ::FFI::Library # FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL mod.ffi_lib_flags :lazy, :global mod.ffi_lib self.library # This class is a little bit of a hack to extract the address of # global structs. If someone knows a better way please let me know. mod.module_eval do self.const_set :DummyStruct, Class.new(::FFI::Struct) self::DummyStruct.layout :dummy_var, :int self.const_set(:PY_FILE_INPUT, 257) self.const_set(:PY_EVAL_INPUT, 258) self.const_set(:METH_VARARGS, 0x0001) # Function methods & constants attach_function :PyCFunction_New, [:pointer, :pointer], :pointer callback :PyCFunction, [:pointer, :pointer], :pointer attach_function :PyRun_String, [:string, :int, :pointer, :pointer], :pointer attach_function :PyRun_SimpleString, [:string], :pointer attach_function :Py_CompileString, [:string, :string, :int], :pointer attach_function :PyEval_EvalCode, [:pointer, :pointer, :pointer], :pointer attach_function :PyErr_SetString, [:pointer, :string], :void # Python interpreter startup and shutdown attach_function :Py_IsInitialized, [], :int attach_function :Py_Initialize, [], :void attach_function :Py_Finalize, [], :void # Module methods attach_function :PyImport_ImportModule, [:string], :pointer # Object Methods attach_function :PyObject_HasAttrString, [:pointer, :string], :int attach_function :PyObject_GetAttrString, [:pointer, :string], :pointer attach_function :PyObject_SetAttrString, [:pointer, :string, :pointer], :int attach_function :PyObject_Dir, [:pointer], :pointer attach_function :PyObject_Compare, [:pointer, :pointer], :int attach_function :PyObject_Call, [:pointer, :pointer, :pointer], :pointer attach_function :PyObject_CallObject, [:pointer, :pointer], :pointer attach_function :PyCallable_Check, [:pointer], :int ### Python To Ruby Conversion # String Methods attach_function :PyString_AsString, [:pointer], :string attach_function :PyString_FromString, [:string], :pointer attach_function :PyString_AsStringAndSize, [:pointer, :pointer, :pointer], :int attach_function :PyString_FromStringAndSize, [:buffer_in, :ssize_t], :pointer # List Methods attach_function :PyList_GetItem, [:pointer, :int], :pointer attach_function :PyList_Size, [:pointer], :int attach_function :PyList_New, [:int], :pointer attach_function :PyList_SetItem, [:pointer, :int, :pointer], :void # Integer Methods attach_function :PyInt_AsLong, [:pointer], :long attach_function :PyInt_FromLong, [:long], :pointer attach_function :PyLong_AsLong, [:pointer], :long attach_function :PyLong_FromLongLong, [:long_long], :pointer # Float Methods attach_function :PyFloat_AsDouble, [:pointer], :double attach_function :PyFloat_FromDouble, [:double], :pointer # Tuple Methods attach_function :PySequence_List, [:pointer], :pointer attach_function :PyList_AsTuple, [:pointer], :pointer attach_function :PyTuple_Pack, [:int, :varargs], :pointer # Dict/Hash Methods attach_function :PyDict_Next, [:pointer, :pointer, :pointer, :pointer], :int attach_function :PyDict_New, [], :pointer attach_function :PyDict_SetItem, [:pointer, :pointer, :pointer], :int attach_function :PyDict_Contains, [:pointer, :pointer], :int attach_function :PyDict_GetItem, [:pointer, :pointer], :pointer # Error Methods attach_variable :PyExc_Exception, self::DummyStruct.by_ref attach_variable :PyExc_StopIteration, self::DummyStruct.by_ref attach_function :PyErr_SetNone, [:pointer], :void attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void attach_function :PyErr_Occurred, [], :pointer attach_function :PyErr_Clear, [], :void # Reference Counting attach_function :Py_IncRef, [:pointer], :void attach_function :Py_DecRef, [:pointer], :void # Type Objects # attach_variable :PyBaseObject_Type, self::DummyStruct.by_value # built-in 'object' # attach_variable :PyBaseString_Type, self::DummyStruct.by_value # attach_variable :PyBool_Type, self::DummyStruct.by_value # attach_variable :PyBuffer_Type, self::DummyStruct.by_value # attach_variable :PyByteArrayIter_Type, self::DummyStruct.by_value # attach_variable :PyByteArray_Type, self::DummyStruct.by_value attach_variable :PyCFunction_Type, self::DummyStruct.by_value # attach_variable :PyCObject_Type, self::DummyStruct.by_value # attach_variable :PyCallIter_Type, self::DummyStruct.by_value # attach_variable :PyCapsule_Type, self::DummyStruct.by_value # attach_variable :PyCell_Type, self::DummyStruct.by_value # attach_variable :PyClassMethod_Type, self::DummyStruct.by_value attach_variable :PyClass_Type, self::DummyStruct.by_value # attach_variable :PyCode_Type, self::DummyStruct.by_value # attach_variable :PyComplex_Type, self::DummyStruct.by_value # attach_variable :PyDictItems_Type, self::DummyStruct.by_value # attach_variable :PyDictIterItem_Type, self::DummyStruct.by_value # attach_variable :PyDictIterKey_Type, self::DummyStruct.by_value # attach_variable :PyDictIterValue_Type, self::DummyStruct.by_value # attach_variable :PyDictKeys_Type, self::DummyStruct.by_value # attach_variable :PyDictProxy_Type, self::DummyStruct.by_value # attach_variable :PyDictValues_Type, self::DummyStruct.by_value attach_variable :PyDict_Type, self::DummyStruct.by_value # attach_variable :PyEllipsis_Type, self::DummyStruct.by_value # attach_variable :PyEnum_Type, self::DummyStruct.by_value # attach_variable :PyFile_Type, self::DummyStruct.by_value attach_variable :PyFloat_Type, self::DummyStruct.by_value # attach_variable :PyFrame_Type, self::DummyStruct.by_value # attach_variable :PyFrozenSet_Type, self::DummyStruct.by_value attach_variable :PyFunction_Type, self::DummyStruct.by_value # attach_variable :PyGen_Type, self::DummyStruct.by_value # attach_variable :PyGetSetDescr_Type, self::DummyStruct.by_value # attach_variable :PyInstance_Type, self::DummyStruct.by_value attach_variable :PyInt_Type, self::DummyStruct.by_value attach_variable :PyList_Type, self::DummyStruct.by_value attach_variable :PyLong_Type, self::DummyStruct.by_value # attach_variable :PyMemberDescr_Type, self::DummyStruct.by_value # attach_variable :PyMemoryView_Type, self::DummyStruct.by_value attach_variable :PyMethod_Type, self::DummyStruct.by_value # attach_variable :PyModule_Type, self::DummyStruct.by_value # attach_variable :PyNullImporter_Type, self::DummyStruct.by_value # attach_variable :PyProperty_Type, self::DummyStruct.by_value # attach_variable :PyRange_Type, self::DummyStruct.by_value # attach_variable :PyReversed_Type, self::DummyStruct.by_value # attach_variable :PySTEntry_Type, self::DummyStruct.by_value # attach_variable :PySeqIter_Type, self::DummyStruct.by_value # attach_variable :PySet_Type, self::DummyStruct.by_value # attach_variable :PySlice_Type, self::DummyStruct.by_value # attach_variable :PyStaticMethod_Type, self::DummyStruct.by_value attach_variable :PyString_Type, self::DummyStruct.by_value # attach_variable :PySuper_Type, self::DummyStruct.by_value # built-in 'super' # attach_variable :PyTraceBack_Type, self::DummyStruct.by_value attach_variable :PyTuple_Type, self::DummyStruct.by_value attach_variable :PyType_Type, self::DummyStruct.by_value # attach_variable :PyUnicode_Type, self::DummyStruct.by_value # attach_variable :PyWrapperDescr_Type, self::DummyStruct.by_value attach_variable :Py_TrueStruct, :_Py_TrueStruct, self::DummyStruct.by_value attach_variable :Py_ZeroStruct, :_Py_ZeroStruct, self::DummyStruct.by_value attach_variable :Py_NoneStruct, :_Py_NoneStruct, self::DummyStruct.by_value # This is an implementation of the basic structure of a Python PyObject # struct. The C struct is actually much larger, but since we only access # the first two data members via FFI and always deal with struct # pointers there is no need to mess around with the rest of the object. self.const_set :PyObjectStruct, Class.new(::FFI::Struct) self::PyObjectStruct.layout :ob_refcnt, :ssize_t, :ob_type, :pointer # This struct is used when defining Python methods. self.const_set :PyMethodDef, Class.new(::FFI::Struct) self::PyMethodDef.layout :ml_name, :pointer, :ml_meth, :PyCFunction, :ml_flags, :int, :ml_doc, :pointer end end
Run a Python command-line command.
# File lib/rubypython/interpreter.rb, line 200 def runpy(command) i = @python || @python_exe || 'python' if ::FFI::Platform.windows? o = %x(#{i} -c "#{command}" 2> NUL:) else o = %x(#{i} -c "#{command}" 2> /dev/null) end [ $?, o.chomp ] end