pycall 0.1.0.alpha.20170711 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +13 -1
- data/CHANGES.md +35 -0
- data/Gemfile +0 -5
- data/README.md +41 -49
- data/Rakefile +22 -1
- data/appveyor.yml +9 -26
- data/examples/classifier_comparison.rb +52 -52
- data/examples/hist.rb +11 -11
- data/examples/notebooks/classifier_comparison.ipynb +51 -66
- data/examples/notebooks/forest_importances.ipynb +26 -49
- data/examples/notebooks/iruby_integration.ipynb +15 -36
- data/examples/notebooks/lorenz_attractor.ipynb +16 -47
- data/examples/notebooks/polar_axes.ipynb +29 -64
- data/examples/notebooks/sum_benchmarking.ipynb +109 -103
- data/examples/notebooks/xkcd_style.ipynb +12 -12
- data/examples/plot_forest_importances_faces.rb +8 -8
- data/examples/sum_benchmarking.rb +15 -19
- data/ext/pycall/extconf.rb +3 -0
- data/ext/pycall/gc.c +74 -0
- data/ext/pycall/libpython.c +217 -0
- data/ext/pycall/pycall.c +2184 -0
- data/ext/pycall/pycall_internal.h +700 -0
- data/ext/pycall/range.c +69 -0
- data/ext/pycall/ruby_wrapper.c +432 -0
- data/lib/pycall.rb +91 -19
- data/lib/pycall/dict.rb +28 -82
- data/lib/pycall/error.rb +10 -0
- data/lib/pycall/import.rb +45 -40
- data/lib/pycall/init.rb +44 -20
- data/lib/pycall/libpython.rb +6 -380
- data/lib/pycall/libpython/finder.rb +170 -0
- data/lib/pycall/list.rb +21 -51
- data/lib/pycall/pretty_print.rb +9 -0
- data/lib/pycall/pyerror.rb +14 -20
- data/lib/pycall/pyobject_wrapper.rb +157 -158
- data/lib/pycall/python/PyCall/__init__.py +1 -0
- data/lib/pycall/python/PyCall/six.py +23 -0
- data/lib/pycall/pytypeobject_wrapper.rb +79 -0
- data/lib/pycall/slice.rb +3 -22
- data/lib/pycall/tuple.rb +1 -7
- data/lib/pycall/version.rb +1 -1
- data/lib/pycall/wrapper_object_cache.rb +61 -0
- data/pycall.gemspec +4 -2
- data/tasks/pycall.rake +7 -0
- metadata +65 -27
- data/lib/pycall/eval.rb +0 -57
- data/lib/pycall/exception.rb +0 -13
- data/lib/pycall/pyobject.rb +0 -58
- data/lib/pycall/ruby_wrapper.rb +0 -137
- data/lib/pycall/type_object.rb +0 -11
- data/lib/pycall/types.rb +0 -19
- data/lib/pycall/utils.rb +0 -106
data/lib/pycall/libpython.rb
CHANGED
@@ -1,386 +1,12 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
require 'pycall/libpython/pyobject_struct'
|
3
|
-
require 'pycall/libpython/pytypeobject_struct'
|
4
|
-
|
5
1
|
module PyCall
|
6
2
|
module LibPython
|
7
|
-
|
8
|
-
|
9
|
-
private_class_method
|
10
|
-
|
11
|
-
def self.find_libpython(python = nil)
|
12
|
-
debug = (ENV['DEBUG_FIND_LIBPYTHON'] == '1')
|
13
|
-
dir_sep = File::ALT_SEPARATOR || File::SEPARATOR
|
14
|
-
python ||= 'python'
|
15
|
-
python_config = investigate_python_config(python)
|
16
|
-
|
17
|
-
v = python_config[:VERSION]
|
18
|
-
libprefix = FFI::Platform::LIBPREFIX
|
19
|
-
libs = []
|
20
|
-
%i(INSTSONAME LDLIBRARY).each do |key|
|
21
|
-
lib = python_config[key]
|
22
|
-
libs << lib << File.basename(lib) if lib
|
23
|
-
end
|
24
|
-
if (lib = python_config[:LIBRARY])
|
25
|
-
libs << File.basename(lib, File.extname(lib))
|
26
|
-
end
|
27
|
-
libs << "#{libprefix}python#{v}" << "#{libprefix}python"
|
28
|
-
libs.uniq!
|
29
|
-
|
30
|
-
$stderr.puts "DEBUG(find_libpython) libs: #{libs.inspect}" if debug
|
31
|
-
|
32
|
-
executable = python_config[:executable]
|
33
|
-
libpaths = [ python_config[:LIBDIR] ]
|
34
|
-
if FFI::Platform.windows?
|
35
|
-
libpaths << File.dirname(executable)
|
36
|
-
else
|
37
|
-
libpaths << File.expand_path('../../lib', executable)
|
38
|
-
end
|
39
|
-
libpaths << python_config[:PYTHONFRAMEWORKPREFIX] if FFI::Platform.mac?
|
40
|
-
exec_prefix = python_config[:exec_prefix]
|
41
|
-
libpaths << exec_prefix << [exec_prefix, 'lib'].join(dir_sep)
|
42
|
-
libpaths.compact!
|
43
|
-
|
44
|
-
$stderr.puts "DEBUG(find_libpython) libpaths: #{libpaths.inspect}" if debug
|
45
|
-
|
46
|
-
unless ENV['PYTHONHOME']
|
47
|
-
# PYTHONHOME tells python where to look for both pure python and binary modules.
|
48
|
-
# When it is set, it replaces both `prefix` and `exec_prefix`
|
49
|
-
# and we thus need to set it to both in case they differ.
|
50
|
-
# This is also what the documentation recommends.
|
51
|
-
# However, they are documented to always be the same on Windows,
|
52
|
-
# where it causes problems if we try to include both.
|
53
|
-
if FFI::Platform.windows?
|
54
|
-
ENV['PYTHONHOME'] = exec_prefix
|
55
|
-
else
|
56
|
-
ENV['PYTHONHOME'] = [python_config[:prefix], exec_prefix].join(':')
|
57
|
-
end
|
58
|
-
|
59
|
-
# Unfortunately, setting PYTHONHOME screws up Canopy's Python distribution?
|
60
|
-
unless system(python, '-c', 'import site', out: File::NULL, err: File::NULL)
|
61
|
-
ENV['PYTHONHOME'] = nil
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Try LIBPYTHON environment variable first.
|
66
|
-
if ENV['LIBPYTHON']
|
67
|
-
if File.file?(ENV['LIBPYTHON'])
|
68
|
-
begin
|
69
|
-
libs = ffi_lib(ENV['LIBPYTHON'])
|
70
|
-
return libs.first
|
71
|
-
rescue LoadError
|
72
|
-
end
|
73
|
-
end
|
74
|
-
$stderr.puts '[WARN] Ignore the wrong libpython location specified in LIBPYTHON environment variable.'
|
75
|
-
end
|
76
|
-
|
77
|
-
# Find libpython (we hope):
|
78
|
-
libsuffix = FFI::Platform::LIBSUFFIX
|
79
|
-
multiarch = python_config[:MULTIARCH] || python_config[:multiarch]
|
80
|
-
libs.each do |lib|
|
81
|
-
libpaths.each do |libpath|
|
82
|
-
# NOTE: File.join doesn't use File::ALT_SEPARATOR
|
83
|
-
libpath_libs = [ [libpath, lib].join(dir_sep) ]
|
84
|
-
libpath_libs << [libpath, multiarch, lib].join(dir_sep) if multiarch
|
85
|
-
libpath_libs.each do |libpath_lib|
|
86
|
-
[
|
87
|
-
libpath_lib,
|
88
|
-
"#{libpath_lib}.#{libsuffix}"
|
89
|
-
].each do |fullname|
|
90
|
-
unless File.file?(fullname)
|
91
|
-
$stderr.puts "DEBUG(find_libpython) Unable to find #{fullname}" if debug
|
92
|
-
next
|
93
|
-
end
|
94
|
-
begin
|
95
|
-
dynlibs = ffi_lib(fullname)
|
96
|
-
$stderr.puts "DEBUG(find_libpython) ffi_lib(#{fullname.inspect}) = #{dynlibs.inspect}" if debug
|
97
|
-
return dynlibs.first
|
98
|
-
rescue LoadError
|
99
|
-
# skip load error
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Find libpython in the system path
|
107
|
-
libs.each do |lib|
|
108
|
-
begin
|
109
|
-
dynlibs = ffi_lib(lib)
|
110
|
-
$stderr.puts "DEBUG(find_libpython) ffi_lib(#{lib.inspect}) = #{dynlibs.inspect}" if debug
|
111
|
-
return dynlibs.first
|
112
|
-
rescue LoadError
|
113
|
-
# skip load error
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def self.investigate_python_config(python)
|
119
|
-
python_env = { 'PYTHONIOENCODING' => 'UTF-8' }
|
120
|
-
IO.popen(python_env, [python, python_investigator_py], 'r') do |io|
|
121
|
-
{}.tap do |config|
|
122
|
-
io.each_line do |line|
|
123
|
-
key, value = line.chomp.split(': ', 2)
|
124
|
-
config[key.to_sym] = value if value != 'None'
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def self.python_investigator_py
|
131
|
-
File.expand_path('../python/investigator.py', __FILE__)
|
132
|
-
end
|
133
|
-
|
134
|
-
ffi_lib_flags :lazy, :global
|
135
|
-
libpython = find_libpython ENV['PYTHON']
|
136
|
-
|
137
|
-
define_singleton_method(:find_symbol) {|name| libpython.find_symbol(name.to_s) }
|
138
|
-
|
139
|
-
attach_function :Py_GetVersion, [], :string
|
140
|
-
PYTHON_DESCRIPTION = LibPython.Py_GetVersion().freeze
|
141
|
-
PYTHON_VERSION = PYTHON_DESCRIPTION.split(' ', 2)[0].freeze
|
142
|
-
|
143
|
-
# --- types ---
|
144
|
-
|
145
|
-
if PYTHON_VERSION < '3.2'
|
146
|
-
typedef :long, :Py_hash_t
|
147
|
-
else
|
148
|
-
typedef :ssize_t, :Py_hash_t
|
149
|
-
end
|
150
|
-
|
151
|
-
# --- global variables ---
|
152
|
-
|
153
|
-
attach_variable :_Py_NoneStruct, PyObjectStruct
|
154
|
-
|
155
|
-
def self.Py_None
|
156
|
-
_Py_NoneStruct
|
157
|
-
end
|
158
|
-
|
159
|
-
attach_variable :PyType_Type, PyTypeObjectStruct
|
160
|
-
|
161
|
-
if libpython.find_variable('PyInt_Type')
|
162
|
-
has_PyInt_Type = true
|
163
|
-
attach_variable :PyInt_Type, PyTypeObjectStruct
|
164
|
-
else
|
165
|
-
has_PyInt_Type = false
|
166
|
-
attach_variable :PyInt_Type, :PyLong_Type, PyTypeObjectStruct
|
167
|
-
end
|
168
|
-
|
169
|
-
attach_variable :PyLong_Type, PyTypeObjectStruct
|
170
|
-
attach_variable :PyBool_Type, PyTypeObjectStruct
|
171
|
-
attach_variable :PyFloat_Type, PyTypeObjectStruct
|
172
|
-
attach_variable :PyComplex_Type, PyTypeObjectStruct
|
173
|
-
attach_variable :PyUnicode_Type, PyTypeObjectStruct
|
174
|
-
|
175
|
-
if libpython.find_symbol('PyString_FromStringAndSize')
|
176
|
-
string_as_bytes = false
|
177
|
-
attach_variable :PyString_Type, PyTypeObjectStruct
|
178
|
-
else
|
179
|
-
string_as_bytes = true
|
180
|
-
attach_variable :PyString_Type, :PyBytes_Type, PyTypeObjectStruct
|
181
|
-
end
|
182
|
-
|
183
|
-
attach_variable :PyList_Type, PyTypeObjectStruct
|
184
|
-
attach_variable :PyTuple_Type, PyTypeObjectStruct
|
185
|
-
attach_variable :PyDict_Type, PyTypeObjectStruct
|
186
|
-
attach_variable :PySet_Type, PyTypeObjectStruct
|
187
|
-
|
188
|
-
attach_variable :PyFunction_Type, PyTypeObjectStruct
|
189
|
-
attach_variable :PyMethod_Type, PyTypeObjectStruct
|
190
|
-
|
191
|
-
# --- exceptions ---
|
192
|
-
|
193
|
-
attach_variable :PyExc_RuntimeError, PyObjectStruct.ptr
|
194
|
-
attach_variable :PyExc_TypeError, PyObjectStruct.ptr
|
195
|
-
|
196
|
-
# --- functions ---
|
197
|
-
|
198
|
-
attach_function :Py_InitializeEx, [:int], :void
|
199
|
-
attach_function :Py_IsInitialized, [], :int
|
200
|
-
attach_function :PySys_SetArgvEx, [:int, :pointer, :int], :void
|
201
|
-
|
202
|
-
# Reference count
|
203
|
-
|
204
|
-
attach_function :Py_IncRef, [PyObjectStruct.by_ref], :void
|
205
|
-
attach_function :Py_DecRef, [PyObjectStruct.by_ref], :void
|
206
|
-
|
207
|
-
# Object
|
208
|
-
|
209
|
-
attach_function :_PyObject_New, [PyTypeObjectStruct.ptr], PyObjectStruct.ptr
|
210
|
-
attach_function :PyObject_RichCompare, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, :int], PyObjectStruct.by_ref
|
211
|
-
attach_function :PyObject_GetAttrString, [PyObjectStruct.by_ref, :string], PyObjectStruct.by_ref
|
212
|
-
attach_function :PyObject_SetAttrString, [PyObjectStruct.by_ref, :string, PyObjectStruct.by_ref], :int
|
213
|
-
attach_function :PyObject_HasAttrString, [PyObjectStruct.by_ref, :string], :int
|
214
|
-
attach_function :PyObject_GetItem, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
215
|
-
attach_function :PyObject_SetItem, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
216
|
-
attach_function :PyObject_Call, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
217
|
-
attach_function :PyObject_IsInstance, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
218
|
-
attach_function :PyObject_Dir, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
219
|
-
attach_function :PyObject_Hash, [PyObjectStruct.by_ref], :Py_hash_t
|
220
|
-
attach_function :PyObject_Repr, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
221
|
-
attach_function :PyObject_Str, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
222
|
-
attach_function :PyObject_Type, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
223
|
-
attach_function :PyCallable_Check, [PyObjectStruct.by_ref], :int
|
224
|
-
|
225
|
-
# Type
|
226
|
-
|
227
|
-
attach_function :PyType_Ready, [PyTypeObjectStruct.ptr], :int
|
228
|
-
|
229
|
-
# Bool
|
230
|
-
|
231
|
-
attach_function :PyBool_FromLong, [:long], PyObjectStruct.by_ref
|
3
|
+
require 'pycall/libpython/finder'
|
232
4
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
attach_function :PyInt_AsSsize_t, :PyLong_AsSsize_t, [PyObjectStruct.by_ref], :ssize_t
|
239
|
-
end
|
240
|
-
|
241
|
-
if has_PyInt_Type
|
242
|
-
attach_function :PyInt_FromSsize_t, [:ssize_t], PyObjectStruct.by_ref
|
243
|
-
else
|
244
|
-
attach_function :PyInt_FromSsize_t, :PyLong_FromSsize_t, [:ssize_t], PyObjectStruct.by_ref
|
245
|
-
end
|
246
|
-
|
247
|
-
# Float
|
248
|
-
|
249
|
-
attach_function :PyFloat_FromDouble, [:double], PyObjectStruct.by_ref
|
250
|
-
attach_function :PyFloat_AsDouble, [PyObjectStruct.by_ref], :double
|
251
|
-
|
252
|
-
# Complex
|
253
|
-
|
254
|
-
attach_function :PyComplex_RealAsDouble, [PyObjectStruct.by_ref], :double
|
255
|
-
attach_function :PyComplex_ImagAsDouble, [PyObjectStruct.by_ref], :double
|
256
|
-
|
257
|
-
# String
|
258
|
-
|
259
|
-
if string_as_bytes
|
260
|
-
attach_function :PyString_FromStringAndSize, :PyBytes_FromStringAndSize, [:string, :ssize_t], PyObjectStruct.by_ref
|
261
|
-
else
|
262
|
-
attach_function :PyString_FromStringAndSize, [:string, :ssize_t], PyObjectStruct.by_ref
|
263
|
-
end
|
264
|
-
|
265
|
-
# PyString_AsStringAndSize :: (PyPtr, char**, int*) -> int
|
266
|
-
if string_as_bytes
|
267
|
-
attach_function :PyString_AsStringAndSize, :PyBytes_AsStringAndSize, [PyObjectStruct.by_ref, :pointer, :pointer], :int
|
268
|
-
else
|
269
|
-
attach_function :PyString_AsStringAndSize, [PyObjectStruct.by_ref, :pointer, :pointer], :int
|
5
|
+
def self.handle
|
6
|
+
# NOTE: PyCall.init redefine this method.
|
7
|
+
# See pycall/init.rb for the detail.
|
8
|
+
PyCall.init
|
9
|
+
handle
|
270
10
|
end
|
271
|
-
|
272
|
-
# Unicode
|
273
|
-
|
274
|
-
# PyUnicode_DecodeUTF8
|
275
|
-
case
|
276
|
-
when libpython.find_symbol('PyUnicode_DecodeUTF8')
|
277
|
-
attach_function :PyUnicode_DecodeUTF8, [:string, :ssize_t, :string], PyObjectStruct.by_ref
|
278
|
-
when libpython.find_symbol('PyUnicodeUCS4_DecodeUTF8')
|
279
|
-
attach_function :PyUnicode_DecodeUTF8, :PyUnicodeUCS4_DecodeUTF8, [:string, :ssize_t, :string], PyObjectStruct.by_ref
|
280
|
-
when libpython.find_symbol('PyUnicodeUCS2_DecodeUTF8')
|
281
|
-
attach_function :PyUnicode_DecodeUTF8, :PyUnicodeUCS2_DecodeUTF8, [:string, :ssize_t, :string], PyObjectStruct.by_ref
|
282
|
-
end
|
283
|
-
|
284
|
-
# PyUnicode_AsUTF8String
|
285
|
-
case
|
286
|
-
when libpython.find_symbol('PyUnicode_AsUTF8String')
|
287
|
-
attach_function :PyUnicode_AsUTF8String, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
288
|
-
when libpython.find_symbol('PyUnicodeUCS4_AsUTF8String')
|
289
|
-
attach_function :PyUnicode_AsUTF8String, :PyUnicodeUCS4_AsUTF8String, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
290
|
-
when libpython.find_symbol('PyUnicodeUCS2_AsUTF8String')
|
291
|
-
attach_function :PyUnicode_AsUTF8String, :PyUnicodeUCS2_AsUTF8String, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
292
|
-
end
|
293
|
-
|
294
|
-
# Tuple
|
295
|
-
|
296
|
-
attach_function :PyTuple_New, [:ssize_t], PyObjectStruct.by_ref
|
297
|
-
attach_function :PyTuple_GetItem, [PyObjectStruct.by_ref, :ssize_t], PyObjectStruct.by_ref
|
298
|
-
attach_function :PyTuple_SetItem, [PyObjectStruct.by_ref, :ssize_t, PyObjectStruct.by_ref], :int
|
299
|
-
attach_function :PyTuple_Size, [PyObjectStruct.by_ref], :ssize_t
|
300
|
-
|
301
|
-
# Slice
|
302
|
-
|
303
|
-
attach_function :PySlice_New, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
304
|
-
|
305
|
-
# List
|
306
|
-
|
307
|
-
attach_function :PyList_New, [:ssize_t], PyObjectStruct.by_ref
|
308
|
-
attach_function :PyList_Size, [PyObjectStruct.by_ref], :ssize_t
|
309
|
-
attach_function :PyList_Append, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
310
|
-
|
311
|
-
# Sequence
|
312
|
-
|
313
|
-
attach_function :PySequence_Size, [PyObjectStruct.by_ref], :ssize_t
|
314
|
-
attach_function :PySequence_GetItem, [PyObjectStruct.by_ref, :ssize_t], PyObjectStruct.by_ref
|
315
|
-
attach_function :PySequence_Contains, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
316
|
-
|
317
|
-
# Dict
|
318
|
-
|
319
|
-
attach_function :PyDict_New, [], PyObjectStruct.by_ref
|
320
|
-
attach_function :PyDict_GetItem, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
321
|
-
attach_function :PyDict_GetItemString, [PyObjectStruct.by_ref, :string], PyObjectStruct.by_ref
|
322
|
-
attach_function :PyDict_SetItem, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
323
|
-
attach_function :PyDict_SetItemString, [PyObjectStruct.by_ref, :string, PyObjectStruct.by_ref], :int
|
324
|
-
attach_function :PyDict_DelItem, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
325
|
-
attach_function :PyDict_DelItemString, [PyObjectStruct.by_ref, :string], :int
|
326
|
-
attach_function :PyDict_Size, [PyObjectStruct.by_ref], :ssize_t
|
327
|
-
attach_function :PyDict_Keys, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
328
|
-
attach_function :PyDict_Values, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
329
|
-
attach_function :PyDict_Items, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
330
|
-
attach_function :PyDict_Contains, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
331
|
-
|
332
|
-
# Set
|
333
|
-
|
334
|
-
attach_function :PySet_Size, [PyObjectStruct.by_ref], :ssize_t
|
335
|
-
attach_function :PySet_Contains, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], :int
|
336
|
-
|
337
|
-
# Method
|
338
|
-
|
339
|
-
attach_function :PyCFunction_NewEx, [PyMethodDef.ptr, :pointer, :pointer], PyObjectStruct.ptr
|
340
|
-
|
341
|
-
# Weakref
|
342
|
-
|
343
|
-
attach_function :PyWeakref_NewRef, [PyObjectStruct.ptr, PyObjectStruct.ptr], PyObjectStruct.ptr
|
344
|
-
|
345
|
-
# Module
|
346
|
-
|
347
|
-
attach_function :PyModule_GetDict, [PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
348
|
-
|
349
|
-
# Import
|
350
|
-
|
351
|
-
attach_function :PyImport_GetModuleDict, [], PyObjectStruct.by_ref
|
352
|
-
attach_function :PyImport_ImportModule, [:string], PyObjectStruct.by_ref
|
353
|
-
attach_function :PyImport_ImportModuleLevel, [:string, PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref, :int], PyObjectStruct.by_ref
|
354
|
-
|
355
|
-
# Operators
|
356
|
-
|
357
|
-
attach_function :PyNumber_Add, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
358
|
-
attach_function :PyNumber_Subtract, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
359
|
-
attach_function :PyNumber_Multiply, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
360
|
-
attach_function :PyNumber_TrueDivide, [PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
361
|
-
attach_function :PyNumber_Power, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
362
|
-
|
363
|
-
# Compiler
|
364
|
-
|
365
|
-
attach_function :Py_CompileString, [:string, :string, :int], PyObjectStruct.by_ref
|
366
|
-
attach_function :PyEval_EvalCode, [PyObjectStruct.by_ref, PyObjectStruct.by_ref, PyObjectStruct.by_ref], PyObjectStruct.by_ref
|
367
|
-
|
368
|
-
# Error
|
369
|
-
|
370
|
-
attach_function :PyErr_Clear, [], :void
|
371
|
-
attach_function :PyErr_Print, [], :void
|
372
|
-
attach_function :PyErr_Occurred, [], PyObjectStruct.by_ref
|
373
|
-
attach_function :PyErr_Fetch, [:pointer, :pointer, :pointer], :void
|
374
|
-
attach_function :PyErr_NormalizeException, [:pointer, :pointer, :pointer], :void
|
375
|
-
attach_function :PyErr_SetString, [PyObjectStruct.ptr, :string], :void
|
376
|
-
|
377
|
-
public_class_method
|
378
|
-
end
|
379
|
-
|
380
|
-
PYTHON_DESCRIPTION = LibPython::PYTHON_DESCRIPTION
|
381
|
-
PYTHON_VERSION = LibPython::PYTHON_VERSION
|
382
|
-
|
383
|
-
def self.unicode_literals?
|
384
|
-
@unicode_literals ||= (PYTHON_VERSION >= '3.0')
|
385
11
|
end
|
386
12
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'pycall/error'
|
2
|
+
require 'fiddle'
|
3
|
+
|
4
|
+
module PyCall
|
5
|
+
module LibPython
|
6
|
+
module Finder
|
7
|
+
case RUBY_PLATFORM
|
8
|
+
when /cygwin/
|
9
|
+
libprefix = 'cyg'
|
10
|
+
libsuffix = 'dll'
|
11
|
+
when /mingw/, /mswin/
|
12
|
+
libprefix = ''
|
13
|
+
libsuffix = 'dll'
|
14
|
+
when /darwin/
|
15
|
+
libsuffix = 'dylib'
|
16
|
+
end
|
17
|
+
|
18
|
+
LIBPREFIX = libprefix || 'lib'
|
19
|
+
LIBSUFFIX = libsuffix || 'so'
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def find_libpython(python = nil)
|
23
|
+
debug_report("find_libpython(#{python.inspect})")
|
24
|
+
if python
|
25
|
+
begin
|
26
|
+
python_config = investigate_python_config(python)
|
27
|
+
rescue
|
28
|
+
raise ::PyCall::PythonNotFound
|
29
|
+
end
|
30
|
+
else
|
31
|
+
%w[python python3].each do |python_cmd|
|
32
|
+
begin
|
33
|
+
python_config = investigate_python_config(python_cmd)
|
34
|
+
python = python_cmd
|
35
|
+
break
|
36
|
+
rescue
|
37
|
+
raise ::PyCall::PythonNotFound
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
libs = make_libs(python_config)
|
43
|
+
libpaths = make_libpaths(python_config)
|
44
|
+
|
45
|
+
# Try LIBPYTHON environment variable first.
|
46
|
+
if (libpython = ENV['LIBPYTHON'])
|
47
|
+
if File.file?(libpython)
|
48
|
+
begin
|
49
|
+
return dlopen(libpython)
|
50
|
+
rescue Fiddle::DLError
|
51
|
+
debug_report "#{$!.class}: #{$!.message}"
|
52
|
+
else
|
53
|
+
debug_report "Success to dlopen #{libpython.inspect} from ENV['LIBPYTHON']"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
warn "WARNING(#{self}.#{__method__}) Ignore the wrong libpython location specified in ENV['LIBPYTHON']."
|
57
|
+
end
|
58
|
+
|
59
|
+
# Find libpython (we hope):
|
60
|
+
multiarch = python_config[:MULTIARCH] || python_config[:multiarch]
|
61
|
+
libs.each do |lib|
|
62
|
+
libpaths.each do |libpath|
|
63
|
+
libpath_libs = [ File.join(libpath, lib) ]
|
64
|
+
libpath_libs << File.join(libpath, multiarch, lib) if multiarch
|
65
|
+
libpath_libs.each do |libpath_lib|
|
66
|
+
[ libpath_lib, "#{libpath_lib}.#{LIBSUFFIX}" ].each do |fullname|
|
67
|
+
unless File.file? fullname
|
68
|
+
debug_report "Unable to find #{fullname}"
|
69
|
+
next
|
70
|
+
end
|
71
|
+
begin
|
72
|
+
return dlopen(libpath_lib)
|
73
|
+
rescue Fiddle::DLError
|
74
|
+
debug_report "#{$!.class}: #{$!.message}"
|
75
|
+
else
|
76
|
+
debug_report "Success to dlopen #{libpaht_lib}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Find libpython in the system path
|
84
|
+
libs.each do |lib|
|
85
|
+
begin
|
86
|
+
return dlopen(lib)
|
87
|
+
rescue Fiddle::DLError
|
88
|
+
debug_report "#{$!.class}: #{$!.message}"
|
89
|
+
else
|
90
|
+
debug_report "Success to dlopen #{lib}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
raise ::PyCall::PythonNotFound
|
95
|
+
end
|
96
|
+
|
97
|
+
def investigate_python_config(python)
|
98
|
+
python_env = { 'PYTHONIOENCODING' => 'UTF-8' }
|
99
|
+
debug_report("investigate_python_config(#{python.inspect})")
|
100
|
+
IO.popen(python_env, [python, python_investigator_py], 'r') do |io|
|
101
|
+
{}.tap do |config|
|
102
|
+
io.each_line do |line|
|
103
|
+
key, value = line.chomp.split(': ', 2)
|
104
|
+
config[key.to_sym] = value if value != 'None'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
rescue Errno::ENOENT
|
109
|
+
raise PyCall::PythonInvestigationFailed
|
110
|
+
end
|
111
|
+
|
112
|
+
def python_investigator_py
|
113
|
+
File.expand_path('../../python/investigator.py', __FILE__)
|
114
|
+
end
|
115
|
+
|
116
|
+
def make_libs(python_config)
|
117
|
+
libs = []
|
118
|
+
%i(INSTSONAME LDLIBRARY).each do |key|
|
119
|
+
lib = python_config[key]
|
120
|
+
libs << lib << File.basename(lib) if lib
|
121
|
+
end
|
122
|
+
if (lib = python_config[:LIBRARY])
|
123
|
+
libs << File.basename(lib, File.extname(lib))
|
124
|
+
end
|
125
|
+
|
126
|
+
v = python_config[:VERSION]
|
127
|
+
libs << "#{LIBPREFIX}python#{v}" << "#{LIBPREFIX}python"
|
128
|
+
libs.uniq!
|
129
|
+
|
130
|
+
debug_report "libs: #{libs.inspect}"
|
131
|
+
return libs
|
132
|
+
end
|
133
|
+
|
134
|
+
def make_libpaths(python_config)
|
135
|
+
executable = python_config[:executable]
|
136
|
+
libpaths = [ python_config[:LIBDIR] ]
|
137
|
+
if Fiddle::WINDOWS
|
138
|
+
libpaths << File.dirname(executable)
|
139
|
+
else
|
140
|
+
libpaths << File.expand_path('../../lib', executable)
|
141
|
+
end
|
142
|
+
libpaths << python_config[:PYTHONFRAMEWORKPREFIX]
|
143
|
+
exec_prefix = python_config[:exec_prefix]
|
144
|
+
libpaths << exec_prefix << File.join(exec_prefix, 'lib')
|
145
|
+
libpaths.compact!
|
146
|
+
|
147
|
+
debug_report "libpaths: #{libpaths.inspect}"
|
148
|
+
return libpaths
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def dlopen(libname)
|
154
|
+
Fiddle.dlopen(libname).tap do |handle|
|
155
|
+
debug_report("dlopen(#{libname.inspect}) = #{handle.inspect}") if handle
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def debug_report(message)
|
160
|
+
return unless debug?
|
161
|
+
$stderr.puts "DEBUG(find_libpython) #{message}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def debug?
|
165
|
+
@debug ||= (ENV['PYCALL_DEBUG_FIND_LIBPYTHON'] == '1')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|