pycall 1.0.1-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +41 -0
- data/CHANGES.md +39 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/Rakefile +29 -0
- data/appveyor.yml +138 -0
- data/bin/console +10 -0
- data/bin/guard +17 -0
- data/bin/rspec +17 -0
- data/bin/runner +6 -0
- data/bin/setup +8 -0
- data/config/Guardfile +30 -0
- data/docker/Dockerfile +191 -0
- data/docker/Gemfile +12 -0
- data/docker/README.md +22 -0
- data/examples/classifier_comparison.rb +135 -0
- data/examples/datascience_rb_20170519.ipynb +4836 -0
- data/examples/hist.rb +32 -0
- data/examples/notebooks/classifier_comparison.ipynb +226 -0
- data/examples/notebooks/forest_importances.ipynb +238 -0
- data/examples/notebooks/iruby_integration.ipynb +183 -0
- data/examples/notebooks/lorenz_attractor.ipynb +214 -0
- data/examples/notebooks/polar_axes.ipynb +209 -0
- data/examples/notebooks/sum_benchmarking.ipynb +374 -0
- data/examples/notebooks/xkcd_style.ipynb +149 -0
- data/examples/plot_forest_importances_faces.rb +46 -0
- data/examples/sum_benchmarking.rb +49 -0
- 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/2.1/pycall.so +0 -0
- data/lib/2.2/pycall.so +0 -0
- data/lib/2.3/pycall.so +0 -0
- data/lib/2.4/pycall.so +0 -0
- data/lib/pycall/conversion.rb +173 -0
- data/lib/pycall/dict.rb +48 -0
- data/lib/pycall/error.rb +10 -0
- data/lib/pycall/gc_guard.rb +84 -0
- data/lib/pycall/import.rb +120 -0
- data/lib/pycall/init.rb +55 -0
- data/lib/pycall/iruby_helper.rb +40 -0
- data/lib/pycall/libpython/finder.rb +170 -0
- data/lib/pycall/libpython/pyobject_struct.rb +30 -0
- data/lib/pycall/libpython/pytypeobject_struct.rb +273 -0
- data/lib/pycall/libpython.rb +12 -0
- data/lib/pycall/list.rb +45 -0
- data/lib/pycall/pretty_print.rb +9 -0
- data/lib/pycall/pyerror.rb +30 -0
- data/lib/pycall/pyobject_wrapper.rb +212 -0
- data/lib/pycall/python/PyCall/__init__.py +1 -0
- data/lib/pycall/python/PyCall/six.py +23 -0
- data/lib/pycall/python/investigator.py +7 -0
- data/lib/pycall/pytypeobject_wrapper.rb +90 -0
- data/lib/pycall/set.rb +19 -0
- data/lib/pycall/slice.rb +8 -0
- data/lib/pycall/tuple.rb +46 -0
- data/lib/pycall/version.rb +3 -0
- data/lib/pycall/wrapper_object_cache.rb +61 -0
- data/lib/pycall.rb +91 -0
- data/pycall.gemspec +40 -0
- data/tasks/docker.rake +21 -0
- data/tasks/pycall.rake +7 -0
- metadata +228 -0
data/lib/pycall/dict.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module PyCall
|
2
|
+
Dict = builtins.dict
|
3
|
+
class Dict
|
4
|
+
register_python_type_mapping
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def self.new(h)
|
9
|
+
super(h, {})
|
10
|
+
end
|
11
|
+
|
12
|
+
def length
|
13
|
+
PyCall.len(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_key?(key)
|
17
|
+
LibPython::Helpers.dict_contains(__pyptr__, key)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias include? has_key?
|
21
|
+
alias key? has_key?
|
22
|
+
alias member? has_key?
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
super
|
26
|
+
rescue PyError
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(key)
|
31
|
+
v = self[key]
|
32
|
+
LibPython::Helpers.delitem(__pyptr__, key)
|
33
|
+
v
|
34
|
+
end
|
35
|
+
|
36
|
+
def each
|
37
|
+
return enum_for unless block_given?
|
38
|
+
LibPython::Helpers.dict_each(__pyptr__, &proc)
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_h
|
43
|
+
inject({}) do |h, (k, v)|
|
44
|
+
h.update(k => v)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/pycall/error.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'pycall'
|
2
|
+
|
3
|
+
module PyCall
|
4
|
+
module GCGuard
|
5
|
+
@gc_guard = {}
|
6
|
+
|
7
|
+
class Key < Struct.new(:pyptr)
|
8
|
+
def initialize(pyptr)
|
9
|
+
self.pyptr = check_pyptr(pyptr)
|
10
|
+
# LibPython.Py_IncRef(pyptr)
|
11
|
+
end
|
12
|
+
|
13
|
+
def release
|
14
|
+
# LibPython.Py_DecRef(pyptr)
|
15
|
+
self.pyptr = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
case other
|
20
|
+
when Key
|
21
|
+
pyptr.pointer == other.pyptr.pointer
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
alias :eql? :==
|
28
|
+
|
29
|
+
def hash
|
30
|
+
pyptr.pointer.address
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def check_pyptr(pyptr)
|
36
|
+
pyptr = pyptr.__pyobj__ if pyptr.respond_to? :__pyobj__
|
37
|
+
return pyptr if pyptr.kind_of? LibPython::PyObjectStruct
|
38
|
+
raise TypeError, "The argument must be a Python object"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.register(pyobj, obj)
|
43
|
+
key = Key.new(pyobj)
|
44
|
+
@gc_guard[key] ||= []
|
45
|
+
@gc_guard[key] << obj
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.unregister(pyobj)
|
49
|
+
key = Key.new(pyobj)
|
50
|
+
@gc_guard.delete(key).tap { key.release }
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.guarded_object_count
|
54
|
+
@gc_guard.length
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.embed(pyobj, obj)
|
58
|
+
pyptr = pyobj.respond_to?(:__pyobj__) ? pyobj.__pyobj__ : pyobj
|
59
|
+
raise TypeError, "The argument must be a Python object" unless pyptr.kind_of? LibPython::PyObjectStruct
|
60
|
+
wo = LibPython.PyWeakref_NewRef(pyptr, weakref_callback)
|
61
|
+
register(wo, obj)
|
62
|
+
pyobj
|
63
|
+
end
|
64
|
+
|
65
|
+
private_class_method def self.weakref_callback
|
66
|
+
unless @weakref_callback
|
67
|
+
@weakref_callback_func = FFI::Function.new(
|
68
|
+
LibPython::PyObjectStruct.ptr,
|
69
|
+
[LibPython::PyObjectStruct.ptr, LibPython::PyObjectStruct.ptr]
|
70
|
+
) do |callback, pyptr|
|
71
|
+
GCGuard.unregister(pyptr)
|
72
|
+
# LibPython.Py_DecRef(pyptr)
|
73
|
+
# LibPython.Py_IncRef(PyCall.None)
|
74
|
+
next PyCall.None
|
75
|
+
end
|
76
|
+
method_def = LibPython::PyMethodDef.new("weakref_callback", @weakref_callback_func, LibPython::METH_O, nil)
|
77
|
+
@weakref_callback = LibPython.PyCFunction_NewEx(method_def, nil, nil).tap {|po| LibPython.Py_IncRef(po) }
|
78
|
+
end
|
79
|
+
@weakref_callback
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private_constant :GCGuard
|
84
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'pycall'
|
2
|
+
|
3
|
+
module PyCall
|
4
|
+
module Import
|
5
|
+
class << self
|
6
|
+
attr_reader :main_object
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
PyCall::Import.instance_variable_set(:@main_object, self)
|
12
|
+
|
13
|
+
module PyCall
|
14
|
+
module Import
|
15
|
+
def pyimport(mod_name, as: nil)
|
16
|
+
as = mod_name unless as
|
17
|
+
check_valid_module_variable_name(mod_name, as)
|
18
|
+
mod = PyCall.import_module(mod_name)
|
19
|
+
define_singleton_method(as) { mod }
|
20
|
+
end
|
21
|
+
|
22
|
+
# This function is implemented as a mimic of `import_from` function defined in `Python/ceval.c`.
|
23
|
+
def pyfrom(mod_name, import: nil)
|
24
|
+
raise ArgumentError, "missing identifier(s) to be imported" unless import
|
25
|
+
|
26
|
+
mod_name = mod_name.to_str if mod_name.respond_to? :to_str
|
27
|
+
mod_name = mod_name.to_s if mod_name.is_a? Symbol
|
28
|
+
|
29
|
+
import = Array(import)
|
30
|
+
fromlist = import.map.with_index do |import_name, i|
|
31
|
+
case import_name
|
32
|
+
when assoc_array_matcher
|
33
|
+
import_name[0]
|
34
|
+
when Symbol, String
|
35
|
+
import_name
|
36
|
+
else
|
37
|
+
raise ArgumentError, "wrong type of import name #{import_name.class} (expected String or Symbol)"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
from_list = PyCall.tuple(from_list)
|
41
|
+
|
42
|
+
main_dict_ptr = PyCall.import_module('__main__').__dict__.__pyptr__
|
43
|
+
globals = main_dict_ptr # FIXME: this should mimic to `import_name` function defined in `Python/ceval.c`.
|
44
|
+
locals = main_dict_ptr # FIXME: this should mimic to `import_name` function defined in `Python/ceval.c`.
|
45
|
+
level = 0 # TODO: support prefixed dots (#25)
|
46
|
+
mod = LibPython::Helpers.import_module(mod_name, globals, locals, fromlist, level)
|
47
|
+
|
48
|
+
import.each do |import_name|
|
49
|
+
case import_name
|
50
|
+
when assoc_array_matcher
|
51
|
+
name, asname = *import_name
|
52
|
+
when Symbol, String
|
53
|
+
name, asname = import_name, import_name
|
54
|
+
end
|
55
|
+
|
56
|
+
if PyCall::LibPython::Helpers.hasattr?(mod.__pyptr__, name)
|
57
|
+
pyobj = PyCall::LibPython::Helpers.getattr(mod.__pyptr__, name)
|
58
|
+
define_name(asname, pyobj)
|
59
|
+
next
|
60
|
+
end
|
61
|
+
|
62
|
+
if mod.respond_to? :__name__
|
63
|
+
pkgname = mod.__name__
|
64
|
+
fullname = "#{pkgname}.#{name}"
|
65
|
+
sys_modules = PyCall.import_module('sys').modules
|
66
|
+
if sys_modules.has_key?(fullname)
|
67
|
+
pyobj = module_dict[fullname]
|
68
|
+
define_name(asname, pyobj)
|
69
|
+
next
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
raise ArgumentError, "cannot import name #{fullname}" unless pyobj
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def define_name(name, pyobj)
|
80
|
+
if callable?(pyobj) && !type_object?(pyobj)
|
81
|
+
define_singleton_method(name) do |*args|
|
82
|
+
LibPython::Helpers.call_object(pyobj.__pyptr__, *args)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
if constant_name?(name)
|
86
|
+
context = self
|
87
|
+
context = (self == PyCall::Import.main_object) ? Object : self
|
88
|
+
context.module_eval { const_set(name, pyobj) }
|
89
|
+
else
|
90
|
+
define_singleton_method(name) { pyobj }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def constant_name?(name)
|
96
|
+
name =~ /\A[A-Z]/
|
97
|
+
end
|
98
|
+
|
99
|
+
def check_valid_module_variable_name(mod_name, var_name)
|
100
|
+
var_name = var_name.to_s if var_name.kind_of? Symbol
|
101
|
+
if var_name.include?('.')
|
102
|
+
raise ArgumentError, "#{var_name} is not a valid module variable name, use pyimport #{mod_name}, as: <name>"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def assoc_array_matcher
|
107
|
+
@assoc_array_matcher ||= ->(ary) do
|
108
|
+
ary.is_a?(Array) && ary.length == 2
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def callable?(pyobj)
|
113
|
+
LibPython::Helpers.callable?(pyobj.__pyptr__)
|
114
|
+
end
|
115
|
+
|
116
|
+
def type_object?(pyobj)
|
117
|
+
pyobj.__pyptr__.kind_of? PyTypePtr
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/pycall/init.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module PyCall
|
2
|
+
def self.const_missing(name)
|
3
|
+
case name
|
4
|
+
when :PyPtr, :PyTypePtr, :PyObjectWrapper, :PYTHON_DESCRIPTION, :PYTHON_VERSION
|
5
|
+
PyCall.init
|
6
|
+
const_get(name)
|
7
|
+
else
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module LibPython
|
13
|
+
def self.const_missing(name)
|
14
|
+
case name
|
15
|
+
when :API, :Conversion, :Helpers, :PYTHON_DESCRIPTION, :PYTHON_VERSION
|
16
|
+
PyCall.init
|
17
|
+
const_get(name)
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.init(python = ENV['PYTHON'])
|
25
|
+
return false if LibPython.instance_variable_defined?(:@handle)
|
26
|
+
class << PyCall
|
27
|
+
remove_method :const_missing
|
28
|
+
end
|
29
|
+
class << PyCall::LibPython
|
30
|
+
remove_method :const_missing
|
31
|
+
end
|
32
|
+
|
33
|
+
ENV['PYTHONPATH'] = [ File.expand_path('../python', __FILE__), ENV['PYTHONPATH'] ].compact.join(File::PATH_SEPARATOR)
|
34
|
+
|
35
|
+
LibPython.instance_variable_set(:@handle, LibPython::Finder.find_libpython(python))
|
36
|
+
class << LibPython
|
37
|
+
undef_method :handle
|
38
|
+
attr_reader :handle
|
39
|
+
end
|
40
|
+
|
41
|
+
begin
|
42
|
+
major, minor, _ = RUBY_VERSION.split('.')
|
43
|
+
require "#{major}.#{minor}/pycall.so"
|
44
|
+
rescue LoadError
|
45
|
+
require 'pycall.so'
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'pycall/dict'
|
49
|
+
require 'pycall/list'
|
50
|
+
require 'pycall/slice'
|
51
|
+
const_set(:PYTHON_VERSION, LibPython::PYTHON_VERSION)
|
52
|
+
const_set(:PYTHON_DESCRIPTION, LibPython::PYTHON_DESCRIPTION)
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'pycall'
|
2
|
+
require 'iruby'
|
3
|
+
|
4
|
+
module PyCall
|
5
|
+
module IRubyHelper
|
6
|
+
private
|
7
|
+
|
8
|
+
def check_pyobject_respond_to_format_method(obj, method_name)
|
9
|
+
return false unless obj.kind_of? PyObject
|
10
|
+
return false unless PyCall.hasattr?(obj, method_name)
|
11
|
+
PyCall.getattr(obj, method_name).kind_of? PyCall::LibPython.PyMethod_Type
|
12
|
+
end
|
13
|
+
|
14
|
+
def register_pyobject_formatter(format_name, mime, priority_value=0)
|
15
|
+
method_name = :"_repr_#{format_name}_"
|
16
|
+
match do |obj|
|
17
|
+
check_pyobject_respond_to_format_method(obj, method_name)
|
18
|
+
end
|
19
|
+
priority priority_value
|
20
|
+
format mime do |obj|
|
21
|
+
PyCall.getattr(obj, method_name).()
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
::IRuby::Display::Registry.module_eval do
|
28
|
+
extend PyCall::IRubyHelper
|
29
|
+
|
30
|
+
register_pyobject_formatter :html, 'text/html'
|
31
|
+
register_pyobject_formatter :markdown, 'text/markdown'
|
32
|
+
register_pyobject_formatter :svg, 'image/svg+xml'
|
33
|
+
register_pyobject_formatter :png, 'image/png'
|
34
|
+
register_pyobject_formatter :jpeg, 'image/jpeg'
|
35
|
+
register_pyobject_formatter :latex, 'text/latex'
|
36
|
+
register_pyobject_formatter :json, 'application/json'
|
37
|
+
register_pyobject_formatter :javascript, 'application/javascript'
|
38
|
+
register_pyobject_formatter :pdf, 'application/pdf'
|
39
|
+
register_pyobject_formatter :pretty, 'text/plain', -1000
|
40
|
+
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
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module PyCall
|
4
|
+
module LibPython
|
5
|
+
class PyObjectStruct < FFI::Struct
|
6
|
+
end
|
7
|
+
|
8
|
+
class PyTypeObjectStruct < PyObjectStruct
|
9
|
+
end
|
10
|
+
|
11
|
+
class PyObjectStruct < FFI::Struct
|
12
|
+
layout ob_refcnt: :ssize_t,
|
13
|
+
ob_type: PyTypeObjectStruct.by_ref
|
14
|
+
|
15
|
+
def self.null
|
16
|
+
new(FFI::Pointer::NULL)
|
17
|
+
end
|
18
|
+
|
19
|
+
def py_none?
|
20
|
+
PyCall.none?(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def kind_of?(klass)
|
24
|
+
klass = klass.__pyobj__ if klass.kind_of? PyObjectWrapper
|
25
|
+
return super unless klass.kind_of? PyObjectStruct
|
26
|
+
PyCall::Types.pyisinstance(self, klass)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|