module PyBind::LibPython

Public Class Methods

Py_False() click to toggle source
# File lib/pybind/libpython.rb, line 208
def self.Py_False
  _Py_ZeroStruct
end
Py_None() click to toggle source
# File lib/pybind/libpython.rb, line 117
def self.Py_None
  _Py_NoneStruct
end
Py_True() click to toggle source
# File lib/pybind/libpython.rb, line 200
def self.Py_True
  _Py_TrueStruct
end
find_libpython(python = nil) click to toggle source
# File lib/pybind/libpython.rb, line 7
def self.find_libpython(python = nil)
  python ||= ENV['PYTHON'] || 'python'
  python_config = investigate_python_config(python)

  version = python_config[:VERSION]
  libprefix = FFI::Platform::LIBPREFIX
  libs = []
  %i(INSTSONAME LDLIBRARY).each do |key|
    lib = python_config[key]
    libs << lib << File.basename(lib) if lib
  end
  if (lib = python_config[:LIBRARY])
    libs << File.basename(lib, File.extname(lib))
  end
  libs << "#{libprefix}python#{version}" << "#{libprefix}python"
  libs.uniq!

  executable = python_config[:EXECUTABLE]
  libpaths = [ python_config[:LIBDIR] ]
  if FFI::Platform.windows?
    libpaths << File.dirname(executable)
  else
    libpaths << File.expand_path('../../lib', executable)
  end
  libpaths << python_config[:PYTHONFRAMEWORKPREFIX] if FFI::Platform.mac?
  exec_prefix = python_config[:EXECPREFIX]
  libpaths << exec_prefix << File.join(exec_prefix, 'lib')
  libpaths.compact!

  unless ENV['PYTHONHOME']
    # PYTHONHOME tells python where to look for both pure python and binary modules.
    # When it is set, it replaces both `prefix` and `exec_prefix`
    # and we thus need to set it to both in case they differ.
    # This is also what the documentation recommends.
    # However, they are documented to always be the same on Windows,
    # where it causes problems if we try to include both.
    if FFI::Platform.windows?
      ENV['PYTHONHOME'] = exec_prefix
    else
      ENV['PYTHONHOME'] = [python_config[:PREFIX], exec_prefix].join(':')
    end

    # Unfortunately, setting PYTHONHOME screws up Canopy's Python distribution?
    unless system(python, '-c', 'import site', out: File::NULL, err: File::NULL)
      ENV['PYTHONHOME'] = nil
    end
  end

  # Try LIBPYTHON environment variable first.
  if ENV['LIBPYTHON']
    if File.file?(ENV['LIBPYTHON'])
      begin
        libs = ffi_lib(ENV['LIBPYTHON'])
        return libs.first
      rescue LoadError
      end
    end
    $stderr.puts '[WARN] Ignore the wrong libpython location specified in LIBPYTHON environment variable.'
  end

  # Find libpython (we hope):
  libsuffix = FFI::Platform::LIBSUFFIX
  multiarch = python_config[:MULTIARCH] || python_config[:IMPLEMENTATIONMULTIARCH]
  dir_sep = File::ALT_SEPARATOR || File::SEPARATOR
  libs.each do |lib|
    libpaths.each do |libpath|
      # NOTE: File.join doesn't use File::ALT_SEPARATOR
      libpath_libs = [ [libpath, lib].join(dir_sep) ]
      libpath_libs << [libpath, multiarch, lib].join(dir_sep) if multiarch
      libpath_libs.each do |libpath_lib|
        [
          libpath_lib,
          "#{libpath_lib}.#{libsuffix}"
        ].each do |fullname|
          next unless File.file?(fullname)
          begin
            libs = ffi_lib(fullname)
            return libs.first
          rescue LoadError
            # skip load error
          end
        end
      end
    end
  end
end
investigate_python_config(python) click to toggle source
# File lib/pybind/libpython.rb, line 94
def self.investigate_python_config(python)
  python_env = { 'PYTHONIOENCODING' => 'UTF-8' }
  IO.popen(python_env, [python, python_investigator_py], 'r') do |io|
    {}.tap do |config|
      io.each_line do |line|
        key, value = line.chomp.split(': ', 2)
        config[key.to_sym] = value if value != 'None'
      end
    end
  end
end
python_investigator_py() click to toggle source
# File lib/pybind/libpython.rb, line 106
def self.python_investigator_py
  File.expand_path('../python/investigator.py', __FILE__)
end