Module: PyCall::LibPython::Finder

Defined in:
lib/pycall/libpython/finder.rb

Constant Summary collapse

LIBPREFIX =
libprefix || 'lib'
LIBSUFFIX =
libsuffix || 'so'
DEFAULT_PYTHON =
[
  -'python3',
  -'python',
].freeze

Class Method Summary collapse

Class Method Details

.find_libpython(python = nil) ⇒ Object

Raises:



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
93
94
95
96
# File 'lib/pycall/libpython/finder.rb', line 39

def find_libpython(python = nil)
  debug_report("find_libpython(#{python.inspect})")
  python, python_config = find_python_config(python)

  # Try LIBPYTHON environment variable first.
  if (libpython = ENV['LIBPYTHON'])
    if File.file?(libpython)
      begin
        return dlopen(libpython)
      rescue Fiddle::DLError
        debug_report "#{$!.class}: #{$!.message}"
      else
        debug_report "Success to dlopen #{libpython.inspect} from ENV['LIBPYTHON']"
      end
    end
    warn "WARNING(#{self}.#{__method__}) Ignore the wrong libpython location specified in ENV['LIBPYTHON']."
  end

  # Find libpython (we hope):
  set_PYTHONHOME(python_config)
  libs = make_libs(python_config)
  libpaths = make_libpaths(python_config)
  multiarch = python_config[:MULTIARCH] || python_config[:multiarch]
  libs.each do |lib|
    libpaths.each do |libpath|
      libpath_libs = [ File.join(libpath, lib) ]
      libpath_libs << File.join(libpath, multiarch, lib) if multiarch
      libpath_libs.each do |libpath_lib|
        [ libpath_lib, "#{libpath_lib}.#{LIBSUFFIX}" ].each do |fullname|
          unless File.file? fullname
            debug_report "Unable to find #{fullname}"
            next
          end
          begin
            return dlopen(libpath_lib)
          rescue Fiddle::DLError
            debug_report "#{$!.class}: #{$!.message}"
          else
            debug_report "Success to dlopen #{libpaht_lib}"
          end
        end
      end
    end
  end

  # Find libpython in the system path
  libs.each do |lib|
    begin
      return dlopen(lib)
    rescue Fiddle::DLError
      debug_report "#{$!.class}: #{$!.message}"
    else
      debug_report "Success to dlopen #{lib}"
    end
  end

  raise ::PyCall::PythonNotFound
end

.find_python_config(python = nil) ⇒ Object

Raises:



27
28
29
30
31
32
33
34
35
36
37
# File 'lib/pycall/libpython/finder.rb', line 27

def find_python_config(python = nil)
  python ||= DEFAULT_PYTHON
  Array(python).each do |python_cmd|
    begin
      python_config = investigate_python_config(python_cmd)
      return [python_cmd, python_config] unless python_config.empty?
    rescue
    end
  end
  raise ::PyCall::PythonNotFound
end

.investigate_python_config(python) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/pycall/libpython/finder.rb', line 98

def investigate_python_config(python)
  python_env = { 'PYTHONIOENCODING' => 'UTF-8' }
  debug_report("investigate_python_config(#{python.inspect})")
  IO.popen(python_env, [python, python_investigator_py], 'r') do |io|
    {}.tap do |config|
      io.each_line do |line|
        next unless line =~ /: /
        key, value = line.chomp.split(': ', 2)
        case value
        when 'True', 'true', 'False', 'false'
          value = (value == 'True' || value == 'true')
        end
        config[key.to_sym] = value if value != 'None'
      end
    end
  end
rescue Errno::ENOENT
  raise PyCall::PythonInvestigationFailed
end

.make_libpaths(python_config) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/pycall/libpython/finder.rb', line 151

def make_libpaths(python_config)
  executable = python_config[:executable]
  libpaths = [ python_config[:LIBDIR] ]
  if Fiddle::WINDOWS
    libpaths << File.dirname(executable)
  else
    libpaths << File.expand_path('../../lib', executable)
  end
  libpaths << python_config[:PYTHONFRAMEWORKPREFIX]
  exec_prefix = python_config[:exec_prefix]
  libpaths << exec_prefix << File.join(exec_prefix, 'lib')
  libpaths.compact!

  debug_report "libpaths: #{libpaths.inspect}"
  return libpaths
end

.make_libs(python_config) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/pycall/libpython/finder.rb', line 133

def make_libs(python_config)
  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

  v = python_config[:VERSION]
  libs << "#{LIBPREFIX}python#{v}" << "#{LIBPREFIX}python"
  libs.uniq!

  debug_report "libs: #{libs.inspect}"
  return libs
end

.python_investigator_pyObject



118
119
120
# File 'lib/pycall/libpython/finder.rb', line 118

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

.set_PYTHONHOME(python_config) ⇒ Object



122
123
124
125
126
127
128
129
130
131
# File 'lib/pycall/libpython/finder.rb', line 122

def set_PYTHONHOME(python_config)
  if !ENV.has_key?('PYTHONHOME')
    case RUBY_PLATFORM
    when /mingw32/, /cygwin/, /mswin/
      ENV['PYTHONHOME'] = python_config[:exec_prefix]
    else
      ENV['PYTHONHOME'] = python_config.values_at(:prefix, :exec_prefix).join(':')
    end
  end
end