Module: PyBind::LibPython

Extended by:
FFI::Library
Defined in:
lib/pybind/libpython.rb

Class Method Summary collapse

Class Method Details

.find_libpython(python = nil) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 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) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
# 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

.Py_FalseObject



208
209
210
# File 'lib/pybind/libpython.rb', line 208

def self.Py_False
  _Py_ZeroStruct
end

.Py_NoneObject



117
118
119
# File 'lib/pybind/libpython.rb', line 117

def self.Py_None
  _Py_NoneStruct
end

.Py_TrueObject



200
201
202
# File 'lib/pybind/libpython.rb', line 200

def self.Py_True
  _Py_TrueStruct
end

.python_investigator_pyObject



106
107
108
# File 'lib/pybind/libpython.rb', line 106

def self.python_investigator_py
  File.expand_path('../python/investigator.py', __FILE__)
end